Tighten SRP stage fallback gate

This commit is contained in:
2026-04-27 12:48:58 +08:00
parent ebe2e0cbc8
commit 3243eea0bf
6 changed files with 100 additions and 5 deletions

View File

@@ -91,6 +91,9 @@ Unity 兼容的公开命名、对象所有权和扩展点。
哪些 stages 存在native 只执行这些声明,不夹带 built-in pipeline policy。
- Hidden fallback 很危险。如果 managed URP stage 声明支持但无法 record失败必须可见。不要静默用
default built-in path 画同一个 stage然后称之为 URP。
- Camera-frame graph dispatch 必须询问所选 pipeline 是否允许 legacy stage fallback。Managed
`ScriptableRenderPipelineHost` 在 stage recorder 或 managed runtime 权威时不允许 fallback此时缺少
recorder、sequence 或 standalone pass 要在 graph recording 阶段失败,而不是排入 built-in fallback。
- 测试体系已经有有价值的覆盖,但还不足以宣称 SRP/URP stack 完全锁定。随着架构边界收口,应补高价值
contract tests现阶段不要为了大范围测试体系重写而暂停架构工作。
@@ -333,3 +336,6 @@ Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。
`CameraFrameStage``CameraFramePlan`
- Final color processing 表示为 policy 和 final output stage而不是 implicit swapchain behavior。
- Object-id rendering 是 top-level tooling pass并由 `XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT` guard。
- Camera-frame graph dispatch 已收紧 legacy fallback gate只有所选 pipeline 明确允许时,未被
render-graph recorder、pass sequence 或 standalone pass 处理的 stage 才能进入 fallback raster pass。
Managed SRP/URP host 不允许 hidden built-in fallback。

View File

@@ -49,6 +49,9 @@ public:
bool SupportsStageRenderGraph(CameraFrameStage stage) const override;
bool SupportsStageRenderGraph(
const RenderPipelineStageSupportContext& context) const override;
bool AllowsCameraFrameStageFallback(CameraFrameStage stage) const override;
bool AllowsCameraFrameStageFallback(
const RenderPipelineStageSupportContext& context) const override;
bool RecordStageRenderGraph(
const RenderPipelineStageRenderGraphContext& context) override;
bool Render(const FrameExecutionContext& executionContext) override;

View File

@@ -103,6 +103,13 @@ public:
const RenderPipelineStageSupportContext& context) const {
return SupportsStageRenderGraph(context.stage);
}
virtual bool AllowsCameraFrameStageFallback(CameraFrameStage) const {
return true;
}
virtual bool AllowsCameraFrameStageFallback(
const RenderPipelineStageSupportContext& context) const {
return AllowsCameraFrameStageFallback(context.stage);
}
virtual bool RecordStageRenderGraph(
const RenderPipelineStageRenderGraphContext&) {
return false;

View File

@@ -4,6 +4,8 @@
#include "Rendering/Execution/Internal/CameraFrameGraph/PassRecorder.h"
#include "Rendering/Execution/Internal/CameraFrameGraph/SequenceRecorder.h"
#include "Rendering/Execution/Internal/CameraFrameGraph/State.h"
#include "Debug/Logger.h"
#include "Rendering/RenderPipeline.h"
#include <array>
@@ -23,6 +25,25 @@ constexpr std::array<CameraFrameStageRecordHandler, 3u> kCameraFrameStageRecordH
&TryRecordCameraFrameStageStandaloneRenderGraphPass
};
RenderPipelineStageSupportContext BuildStageSupportContext(
const CameraFrameStageGraphBuildState& stageState,
const CameraFrameRenderGraphStageContext& context) {
return RenderPipelineStageSupportContext{
stageState.stage,
context.plan.request.rendererIndex };
}
bool AllowsCameraFrameStageFallback(
const CameraFrameStageGraphBuildState& stageState,
const CameraFrameRenderGraphStageContext& context) {
RenderPipeline* const pipeline = context.builder.executionState.pipeline;
return pipeline == nullptr ||
pipeline->AllowsCameraFrameStageFallback(
BuildStageSupportContext(
stageState,
context));
}
} // namespace
bool RecordCameraFrameRenderGraphStage(
@@ -44,6 +65,15 @@ bool RecordCameraFrameRenderGraphStage(
}
}
if (!AllowsCameraFrameStageFallback(stageState, context)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
Containers::String(
"CameraRenderer::Render failed: stage has no render-graph recorder or sequence, and the selected pipeline disallows fallback: ") +
stageState.stageName);
return false;
}
AddCameraFrameStageFallbackRasterPass(stageState, context);
return true;
}

View File

@@ -118,6 +118,24 @@ bool ScriptableRenderPipelineHost::SupportsStageRenderGraph(
m_pipelineBackend->SupportsStageRenderGraph(context);
}
bool ScriptableRenderPipelineHost::AllowsCameraFrameStageFallback(
CameraFrameStage stage) const {
return AllowsCameraFrameStageFallback(
RenderPipelineStageSupportContext{ stage, -1 });
}
bool ScriptableRenderPipelineHost::AllowsCameraFrameStageFallback(
const RenderPipelineStageSupportContext& context) const {
if (UsesAuthoritativeStageRecorder(
m_stageRecorder.get(),
m_managedAssetRuntime.get())) {
return false;
}
return m_pipelineBackend != nullptr &&
m_pipelineBackend->AllowsCameraFrameStageFallback(context);
}
bool ScriptableRenderPipelineHost::RecordStageRenderGraph(
const RenderPipelineStageRenderGraphContext& context) {
if (!EnsureInitialized(context.renderContext)) {

View File

@@ -8,6 +8,7 @@
#include "Rendering/Execution/Internal/CameraFrameGraph/BuilderContext.h"
#include "Rendering/Execution/Internal/CameraFrameGraph/StageContract.h"
#include "Rendering/Execution/Internal/CameraFrameGraph/StageDispatch.h"
#include "Rendering/Execution/Internal/CameraFrameGraph/ExecutionState.h"
#include "Rendering/Graph/RenderGraph.h"
@@ -102,7 +103,13 @@ public:
}
bool SupportsStageRenderGraph(CameraFrameStage stage) const override {
return SupportsCameraFramePipelineGraphRecording(stage);
return supportsMainSceneRenderGraph &&
SupportsCameraFramePipelineGraphRecording(stage);
}
bool AllowsCameraFrameStageFallback(CameraFrameStage stage) const override {
(void)stage;
return allowsStageFallback;
}
bool RecordStageRenderGraph(
@@ -138,6 +145,8 @@ public:
bool recordResult = true;
bool renderResult = true;
bool supportsMainSceneRenderGraph = true;
bool allowsStageFallback = true;
int recordCalls = 0;
int renderCalls = 0;
std::string lastPassName = {};
@@ -443,22 +452,26 @@ TEST(CameraFrameRenderGraphStageContract_Test, ResolvesStandaloneStagePassFromEx
EXPECT_EQ(
ResolveCameraFrameStandaloneStagePass(
CameraFrameStage::ObjectId,
executionState),
executionState,
-1),
objectIdPass);
EXPECT_EQ(
ResolveCameraFrameStandaloneStagePass(
CameraFrameStage::DepthOnly,
executionState),
executionState,
-1),
depthOnlyPass);
EXPECT_EQ(
ResolveCameraFrameStandaloneStagePass(
CameraFrameStage::ShadowCaster,
executionState),
executionState,
-1),
shadowCasterPass);
EXPECT_EQ(
ResolveCameraFrameStandaloneStagePass(
CameraFrameStage::MainScene,
executionState),
executionState,
-1),
nullptr);
}
@@ -1547,3 +1560,21 @@ TEST(CameraFrameRenderGraphStageContract_Test, ExecutesDepthOnlyFallbackPassUsin
EXPECT_EQ(depthOnlyPass->lastExecuteSceneData.cameraData.clearFlags, RenderClearFlags::Color);
}
TEST(CameraFrameRenderGraphStageContract_Test, FailsRecordingWhenSelectedPipelineDisallowsStageFallback) {
StageContractTestContext testContext = {};
RecordingPipeline pipeline = {};
pipeline.supportsMainSceneRenderGraph = false;
pipeline.allowsStageFallback = false;
testContext.executionState.pipeline = &pipeline;
testContext.plan.request.surface = RenderSurface(320, 180);
const CameraFrameRenderGraphStageContext context =
testContext.BuildStageContext();
EXPECT_FALSE(RecordCameraFrameRenderGraphStage(
CameraFrameStage::MainScene,
context));
EXPECT_EQ(pipeline.recordCalls, 0);
EXPECT_EQ(pipeline.renderCalls, 0);
EXPECT_EQ(testContext.graph.GetPassCount(), 0u);
}