Rendering: formalize main-scene graph contract

This commit is contained in:
2026-04-15 07:56:00 +08:00
parent 8b156dd112
commit 2cda9fd24a
9 changed files with 276 additions and 59 deletions

View File

@@ -507,6 +507,7 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraph.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphBlackboard.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphRecordingContext.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPipelineMainSceneGraphContract.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/CameraFrameRenderGraphFrameData.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphCompiler.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphExecutor.h
@@ -562,6 +563,7 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/Internal/CameraFrameRenderGraphSurfaceUtils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/DirectionalShadowSurfaceCache.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderPassGraphContract.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderPipelineMainSceneGraphContract.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

View File

@@ -81,6 +81,7 @@ inline RenderPipelineMainSceneRenderGraphContext BuildRenderPipelineMainSceneRen
common.sourceSurface,
common.sourceColorView,
common.sourceColorState,
common.sourceColorTexture,
common.colorTargets,
common.depthTarget,
common.executionSucceeded,

View File

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

View File

@@ -0,0 +1,38 @@
#pragma once
#include <XCEngine/Rendering/Execution/ScenePhase.h>
#include <XCEngine/Rendering/Execution/SceneRenderInjectionPoint.h>
#include <XCEngine/Rendering/RenderPassGraphContract.h>
#include <XCEngine/Rendering/RenderPipeline.h>
#include <XCEngine/Rendering/SceneRenderFeaturePass.h>
#include <vector>
namespace XCEngine {
namespace Rendering {
class SceneRenderFeatureHost;
Containers::String BuildRenderPipelineMainScenePhasePassName(
const Containers::String& baseName,
ScenePhase scenePhase);
bool RecordRenderPipelineMainSceneFeaturePasses(
const RenderPipelineMainSceneRenderGraphContext& context,
SceneRenderFeatureHost& featureHost,
SceneRenderInjectionPoint injectionPoint,
bool clearAttachments,
SceneRenderFeaturePassBeginCallback beginPassCallback = {},
SceneRenderFeaturePassEndCallback endPassCallback = {},
bool* recordedAnyPass = nullptr);
bool RecordRenderPipelineMainScenePhasePass(
const RenderPipelineMainSceneRenderGraphContext& context,
ScenePhase scenePhase,
RenderPassGraphExecutePassCallback executePassCallback,
RenderPassGraphBeginCallback beginPassCallback = {},
RenderPassGraphEndCallback endPassCallback = {},
std::vector<RenderGraphTextureHandle> additionalReadTextures = {});
} // namespace Rendering
} // namespace XCEngine

View File

@@ -2,14 +2,12 @@
#include "Debug/Logger.h"
#include "Rendering/Graph/RenderGraph.h"
#include <XCEngine/Rendering/Graph/RenderGraphRecordingContext.h>
#include <XCEngine/Rendering/RenderPassGraphContract.h>
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
#include "Rendering/Pipelines/Internal/BuiltinForwardSceneSetup.h"
#include "Rendering/RenderSurface.h"
#include <XCEngine/Rendering/RenderPipelineMainSceneGraphContract.h>
#include <memory>
#include <string>
namespace XCEngine {
namespace Rendering {
@@ -28,15 +26,6 @@ struct ForwardSceneGraphExecutionState {
bool initialized = false;
};
Containers::String BuildForwardScenePhasePassName(
const Containers::String& baseName,
ScenePhase scenePhase) {
std::string name = baseName.CStr();
name += '.';
name += ToString(scenePhase);
return Containers::String(name.c_str());
}
bool ScenePhaseSamplesMainDirectionalShadow(ScenePhase scenePhase) {
return scenePhase == ScenePhase::Opaque ||
scenePhase == ScenePhase::Transparent;
@@ -47,10 +36,7 @@ bool ScenePhaseSamplesMainDirectionalShadow(ScenePhase scenePhase) {
bool BuiltinForwardMainSceneGraphBuilder::Record(
BuiltinForwardPipeline& pipeline,
const RenderPipelineMainSceneRenderGraphContext& context) {
const Containers::String passName = context.passName;
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 bool hasSourceSurface = context.sourceSurface != nullptr;
@@ -58,8 +44,6 @@ bool BuiltinForwardMainSceneGraphBuilder::Record(
hasSourceSurface ? *context.sourceSurface : RenderSurface();
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 CameraFrameRenderGraphResources* const frameResources =
TryGetCameraFrameRenderGraphResources(context.blackboard);
const RenderGraphTextureHandle mainDirectionalShadowTexture =
@@ -67,13 +51,27 @@ bool BuiltinForwardMainSceneGraphBuilder::Record(
? frameResources->mainDirectionalShadow
: RenderGraphTextureHandle{};
bool* const executionSucceeded = context.executionSucceeded;
const RenderPipelineMainSceneRenderGraphContext graphContext = {
context.graphBuilder,
context.passName,
renderContext,
context.sceneData,
surfaceTemplate,
hasSourceSurface ? &sourceSurface : nullptr,
sourceColorView,
sourceColorState,
context.sourceColorTexture,
context.colorTargets,
context.depthTarget,
executionSucceeded,
context.blackboard
};
const std::shared_ptr<ForwardSceneGraphExecutionState> graphExecutionState =
std::make_shared<ForwardSceneGraphExecutionState>();
const SceneRenderFeaturePassBeginCallback beginRecordedPass =
[&pipeline,
graphExecutionState,
renderContext,
sceneData,
hasSourceSurface,
sourceSurface,
sourceColorView,
@@ -97,9 +95,9 @@ bool BuiltinForwardMainSceneGraphBuilder::Record(
}
const FrameExecutionContext executionContext(
renderContext,
passContext.renderContext,
passContext.surface,
*sceneData,
passContext.sceneData,
hasSourceSurface ? &sourceSurface : nullptr,
sourceColorView,
sourceColorState);
@@ -132,35 +130,18 @@ bool BuiltinForwardMainSceneGraphBuilder::Record(
[&pipeline](const RenderPassContext& passContext) {
pipeline.EndForwardScenePass(passContext);
};
const ::XCEngine::Rendering::RenderGraphRecordingContext commonContext = {
context.graphBuilder,
passName,
renderContext,
*sceneData,
surfaceTemplate,
hasSourceSurface ? &sourceSurface : nullptr,
sourceColorView,
sourceColorState,
{},
colorTargets,
depthTarget,
executionSucceeded,
context.blackboard
};
bool clearAttachments = true;
for (const ForwardSceneStep& step : GetBuiltinForwardSceneSteps()) {
if (step.type == ForwardSceneStepType::InjectionPoint) {
const SceneRenderFeaturePassRenderGraphContext featureContext =
::XCEngine::Rendering::BuildSceneRenderFeaturePassRenderGraphContext(
commonContext,
bool recordedAnyPass = false;
if (!::XCEngine::Rendering::RecordRenderPipelineMainSceneFeaturePasses(
graphContext,
pipeline.m_forwardSceneFeatureHost,
step.injectionPoint,
clearAttachments,
beginRecordedPass,
endRecordedPass);
bool recordedAnyPass = false;
if (!pipeline.m_forwardSceneFeatureHost.Record(
featureContext,
step.injectionPoint,
endRecordedPass,
&recordedAnyPass)) {
return false;
}
@@ -171,8 +152,6 @@ bool BuiltinForwardMainSceneGraphBuilder::Record(
continue;
}
const Containers::String phasePassName =
BuildForwardScenePhasePassName(passName, step.scenePhase);
const RenderPassGraphBeginCallback beginPhasePass =
[beginRecordedPass, clearAttachments](const RenderPassContext& passContext) {
return beginRecordedPass(passContext, clearAttachments);
@@ -182,20 +161,9 @@ bool BuiltinForwardMainSceneGraphBuilder::Record(
mainDirectionalShadowTexture.IsValid()
? std::vector<RenderGraphTextureHandle>{ mainDirectionalShadowTexture }
: std::vector<RenderGraphTextureHandle>{};
::XCEngine::Rendering::RenderGraphRecordingContext phaseCommonContext = commonContext;
phaseCommonContext.passName = phasePassName;
const RenderPassRenderGraphContext phaseContext =
::XCEngine::Rendering::BuildRenderPassRenderGraphContext(
phaseCommonContext,
beginPhasePass,
endRecordedPass);
if (!::XCEngine::Rendering::RecordCallbackRasterRenderPass(
phaseContext,
{
false,
true,
depthTarget.IsValid()
},
if (!::XCEngine::Rendering::RecordRenderPipelineMainScenePhasePass(
graphContext,
step.scenePhase,
[&pipeline, scenePhase = step.scenePhase](const RenderPassContext& passContext) {
const FrameExecutionContext executionContext(
passContext.renderContext,
@@ -208,6 +176,8 @@ bool BuiltinForwardMainSceneGraphBuilder::Record(
pipeline.BuildScenePhaseExecutionContext(executionContext, scenePhase);
return pipeline.ExecuteBuiltinScenePhase(scenePhaseExecutionContext);
},
beginPhasePass,
endRecordedPass,
additionalReadTextures)) {
return false;
}

View File

@@ -0,0 +1,95 @@
#include <XCEngine/Rendering/RenderPipelineMainSceneGraphContract.h>
#include <XCEngine/Rendering/Graph/RenderGraphRecordingContext.h>
#include <XCEngine/Rendering/SceneRenderFeatureHost.h>
#include <string>
#include <utility>
namespace XCEngine {
namespace Rendering {
namespace {
RenderGraphRecordingContext BuildCommonContext(
const RenderPipelineMainSceneRenderGraphContext& context,
Containers::String passName) {
return {
context.graphBuilder,
passName,
context.renderContext,
context.sceneData,
context.surfaceTemplate,
context.sourceSurface,
context.sourceColorView,
context.sourceColorState,
context.sourceColorTexture,
context.colorTargets,
context.depthTarget,
context.executionSucceeded,
context.blackboard
};
}
} // namespace
Containers::String BuildRenderPipelineMainScenePhasePassName(
const Containers::String& baseName,
ScenePhase scenePhase) {
std::string name = baseName.CStr();
if (!name.empty()) {
name += '.';
}
name += ToString(scenePhase);
return Containers::String(name.c_str());
}
bool RecordRenderPipelineMainSceneFeaturePasses(
const RenderPipelineMainSceneRenderGraphContext& context,
SceneRenderFeatureHost& featureHost,
SceneRenderInjectionPoint injectionPoint,
bool clearAttachments,
SceneRenderFeaturePassBeginCallback beginPassCallback,
SceneRenderFeaturePassEndCallback endPassCallback,
bool* recordedAnyPass) {
const SceneRenderFeaturePassRenderGraphContext featureContext =
BuildSceneRenderFeaturePassRenderGraphContext(
BuildCommonContext(context, context.passName),
clearAttachments,
beginPassCallback,
endPassCallback);
return featureHost.Record(
featureContext,
injectionPoint,
recordedAnyPass);
}
bool RecordRenderPipelineMainScenePhasePass(
const RenderPipelineMainSceneRenderGraphContext& context,
ScenePhase scenePhase,
RenderPassGraphExecutePassCallback executePassCallback,
RenderPassGraphBeginCallback beginPassCallback,
RenderPassGraphEndCallback endPassCallback,
std::vector<RenderGraphTextureHandle> additionalReadTextures) {
const RenderPassRenderGraphContext passContext =
BuildRenderPassRenderGraphContext(
BuildCommonContext(
context,
BuildRenderPipelineMainScenePhasePassName(
context.passName,
scenePhase)),
beginPassCallback,
endPassCallback);
return RecordCallbackRasterRenderPass(
passContext,
{
false,
true,
context.depthTarget.IsValid()
},
std::move(executePassCallback),
std::move(additionalReadTextures));
}
} // namespace Rendering
} // namespace XCEngine

View File

@@ -643,6 +643,7 @@ TEST(BuiltinForwardPipeline_Test, RecordsMainSceneGraphPassWithSampledShadowDepe
nullptr,
nullptr,
ResourceStates::Common,
{},
{ colorTarget },
depthTarget,
&executionSucceeded,
@@ -1181,6 +1182,7 @@ TEST(BuiltinForwardPipeline_Test, RecordsActiveFeatureInjectionPassesIntoMainSce
nullptr,
nullptr,
ResourceStates::Common,
{},
{ colorTarget },
depthTarget,
&executionSucceeded,
@@ -1201,6 +1203,112 @@ TEST(BuiltinForwardPipeline_Test, RecordsActiveFeatureInjectionPassesIntoMainSce
EXPECT_STREQ(compiledGraph.GetPassName(4).CStr(), "MainScene.Transparent");
}
TEST(BuiltinForwardPipeline_Test, ForwardsSourceColorTextureIntoFeatureInjectionGraphContext) {
BuiltinForwardPipeline pipeline;
pipeline.m_forwardSceneFeatureHost.m_featurePasses.clear();
auto feature = std::make_unique<TestSceneRenderFeaturePass>(
SceneRenderInjectionPoint::BeforeOpaque,
true,
nullptr,
"SourceAware");
TestSceneRenderFeaturePass* featureRaw = feature.get();
pipeline.AddForwardSceneFeaturePass(std::move(feature));
RenderGraph graph = {};
RenderGraphBuilder graphBuilder(graph);
RenderGraphTextureDesc colorDesc = {};
colorDesc.width = 320u;
colorDesc.height = 180u;
colorDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
colorDesc.textureType = static_cast<uint32_t>(XCEngine::RHI::TextureType::Texture2D);
colorDesc.sampleCount = 1u;
RenderGraphTextureDesc depthDesc = {};
depthDesc.width = 320u;
depthDesc.height = 180u;
depthDesc.format = static_cast<uint32_t>(Format::D24_UNorm_S8_UInt);
depthDesc.textureType = static_cast<uint32_t>(XCEngine::RHI::TextureType::Texture2D);
depthDesc.sampleCount = 1u;
TestResourceView sourceRenderTargetView(
ResourceViewType::RenderTarget,
ResourceViewDimension::Texture2D,
Format::R8G8B8A8_UNorm);
TestResourceView sourceShaderView(
ResourceViewType::ShaderResource,
ResourceViewDimension::Texture2D,
Format::R8G8B8A8_UNorm);
TestResourceView colorView(
ResourceViewType::RenderTarget,
ResourceViewDimension::Texture2D,
Format::R8G8B8A8_UNorm);
TestResourceView depthView(
ResourceViewType::DepthStencil,
ResourceViewDimension::Texture2D,
Format::D24_UNorm_S8_UInt);
const RenderGraphImportedTextureOptions graphManagedImport = {
ResourceStates::Common,
ResourceStates::Common,
true
};
const RenderGraphTextureHandle sourceColor =
graphBuilder.ImportTexture("SourceColor", colorDesc, &sourceRenderTargetView, graphManagedImport);
const RenderGraphTextureHandle colorTarget =
graphBuilder.ImportTexture("MainColor", colorDesc, &colorView, graphManagedImport);
const RenderGraphTextureHandle depthTarget =
graphBuilder.ImportTexture("MainDepth", depthDesc, &depthView, graphManagedImport);
RenderSurface surface(320u, 180u);
surface.SetColorAttachment(&colorView);
surface.SetDepthAttachment(&depthView);
RenderSurface sourceSurface(320u, 180u);
sourceSurface.SetColorAttachment(&sourceRenderTargetView);
RenderContext renderContext = {};
RenderSceneData sceneData = {};
RenderGraphBlackboard blackboard = {};
bool executionSucceeded = true;
const RenderPipelineMainSceneRenderGraphContext context = {
graphBuilder,
"MainScene",
renderContext,
sceneData,
surface,
&sourceSurface,
&sourceShaderView,
ResourceStates::Common,
sourceColor,
{ colorTarget },
depthTarget,
&executionSucceeded,
&blackboard
};
ASSERT_TRUE(pipeline.RecordMainSceneRenderGraph(context));
EXPECT_EQ(featureRaw->recordGraphCallCount, 1u);
EXPECT_TRUE(featureRaw->lastReceivedSourceColorTextureValid);
CompiledRenderGraph compiledGraph = {};
String errorMessage;
ASSERT_TRUE(RenderGraphCompiler::Compile(graph, compiledGraph, &errorMessage))
<< errorMessage.CStr();
ASSERT_EQ(compiledGraph.GetPassCount(), 4u);
EXPECT_STREQ(compiledGraph.GetPassName(0).CStr(), "MainScene.BeforeOpaque.SourceAware.0");
EXPECT_STREQ(compiledGraph.GetPassName(1).CStr(), "MainScene.Opaque");
EXPECT_STREQ(compiledGraph.GetPassName(2).CStr(), "MainScene.Skybox");
EXPECT_STREQ(compiledGraph.GetPassName(3).CStr(), "MainScene.Transparent");
RenderGraphTextureLifetime sourceLifetime = {};
ASSERT_TRUE(compiledGraph.TryGetTextureLifetime(sourceColor, sourceLifetime));
EXPECT_TRUE(sourceLifetime.used);
EXPECT_EQ(sourceLifetime.firstPassIndex, 0u);
EXPECT_EQ(sourceLifetime.lastPassIndex, 0u);
}
TEST(BuiltinForwardPipeline_Test, PropagatesForwardDrawFailureWhenPipelineStateCreationFails) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinForwardLitShaderPath());

View File

@@ -3676,6 +3676,7 @@ TEST(ScriptableRenderPipelineHost_Test, ForwardsRendererLifetimeAndFrameRenderin
XCEngine::RHI::ResourceStates::Common,
{},
{},
{},
&executionSucceeded,
&blackboard
};

View File

@@ -198,6 +198,7 @@ TEST(RenderGraphRecordingContext_Test, BuildsPipelineMainSceneContextFromSharedR
EXPECT_EQ(
pipelineContext.sourceColorState,
XCEngine::RHI::ResourceStates::PixelShaderResource);
EXPECT_EQ(pipelineContext.sourceColorTexture.index, 3u);
ASSERT_EQ(pipelineContext.colorTargets.size(), 2u);
EXPECT_EQ(pipelineContext.colorTargets[1].index, 5u);
EXPECT_EQ(pipelineContext.depthTarget.index, 6u);