Close URP stage planning authority

This commit is contained in:
2026-04-28 00:38:52 +08:00
parent 43a71bd09c
commit 2d91085567
4 changed files with 59 additions and 72 deletions

View File

@@ -97,6 +97,9 @@ Unity 兼容的公开命名、对象所有权和扩展点。
built-in forward pipeline policy而不是作为 URP-declared work 的 backend executor。
- 目标所有权模型是URP 决定渲染什么、何时渲染、哪些 renderer lists/passes/features 激活,以及
哪些 stages 存在native 只执行这些声明,不夹带 built-in pipeline policy。
- `CameraFramePlanBuilder` 现在只负责从 request 构造 `CameraFramePlan` 并委托所选
`RenderPipelineAsset::ConfigureCameraFramePlan`。它不再在 asset hook 之后追加通用 legacy
fullscreen stage heuristic默认 native fullscreen/final-output 行为应留在 native asset policy 内部。
- Hidden fallback 很危险。如果 managed URP stage 声明支持但无法 record失败必须可见。不要静默用
default built-in path 画同一个 stage然后称之为 URP。
- Camera-frame graph dispatch 必须询问所选 pipeline 是否允许 legacy stage fallback。Managed
@@ -246,6 +249,9 @@ package。
- URP stage planning 以 `ScriptableRenderer` 的 active pass queue 为最终事实源。`ConfigureCameraFramePlan`
仍是兼容和高级策略 hook但它不能单独声明 shadow、depth、post 或 final-output stage没有被 pass queue
覆盖到的 side/fullscreen stage 必须在最终 plan 中清掉。
- Native `CameraFramePlanBuilder` 不得在 renderer/pass-queue-derived manifest 应用之后再补
fullscreen stage 请求URP path 中 post/final stage 的存在性必须完全来自 `ScriptableRenderer`
active pass queue。
- `ConfigurePassQueueCameraFramePlanInstance` 必须在 planning 阶段为当前 renderer 和 `framePlanId`
生成一次 per-camera `RendererFramePlan`,冻结 active pass queue 中每个 pass 的当帧状态,并用同一份 plan
派生 stage manifest。`SupportsStageRenderGraph`
@@ -400,6 +406,9 @@ Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。
- URP stage planning 已收口到 renderer active pass queue 派生的 per-`framePlanId` `RendererFramePlan`
stage manifest。Stage support 和 stage recording 现在消费 planning 阶段保存的同一份冻结 plan
关闭了 feature planning hook、support probe、recording 和各 stage 分别重建 pass queue 的重复事实源。
- `CameraFramePlanBuilder` 已移除对 fullscreen stages 的通用尾部自动补齐selected
`RenderPipelineAsset` 现在是 native plan policy 的唯一入口managed URP path 不再保留第二个
fullscreen stage planner。
- `ScriptableRenderPass.CreateFramePlanSnapshot` 已接入 `RendererFramePlan` 生成路径。多 camera planning 会冻结
每个 camera 当时的 pass 状态,后续 camera 对复用 pass 实例的 `Configure` 不应污染已生成的 frame plan。
- URP runtime-state invalidation 已覆盖 asset-level shadow/final-color settings 和 renderer-data-level

View File

@@ -5,62 +5,6 @@
namespace XCEngine {
namespace Rendering {
namespace {
bool UsesExplicitFullscreenSource(
const FullscreenPassRenderRequest& request,
CameraFrameColorSource source) {
return source == CameraFrameColorSource::ExplicitSurface &&
HasValidColorTarget(request.sourceSurface);
}
void ConfigureLegacyFullscreenStageRequests(
CameraFramePlan& plan) {
const bool postProcessExplicitlyConfigured =
plan.HasExplicitFullscreenStageConfiguration(
CameraFrameStage::PostProcess);
const bool finalOutputExplicitlyConfigured =
plan.HasExplicitFullscreenStageConfiguration(
CameraFrameStage::FinalOutput);
const bool hasPostProcess =
plan.postProcess.IsRequested();
const bool hasFinalOutput =
plan.finalOutput.IsRequested();
if (!postProcessExplicitlyConfigured &&
hasPostProcess &&
!UsesExplicitFullscreenSource(
plan.postProcess,
plan.ResolveStageColorSource(
CameraFrameStage::PostProcess))) {
plan.RequestFullscreenStage(
CameraFrameStage::PostProcess,
CameraFrameColorSource::MainSceneColor,
hasFinalOutput);
}
const bool canChainFromPostProcess =
hasPostProcess &&
!UsesExplicitFullscreenSource(
plan.postProcess,
plan.ResolveStageColorSource(
CameraFrameStage::PostProcess));
if (!finalOutputExplicitlyConfigured &&
hasFinalOutput &&
!UsesExplicitFullscreenSource(
plan.finalOutput,
plan.ResolveStageColorSource(
CameraFrameStage::FinalOutput))) {
plan.RequestFullscreenStage(
CameraFrameStage::FinalOutput,
canChainFromPostProcess
? CameraFrameColorSource::PostProcessColor
: CameraFrameColorSource::MainSceneColor);
}
}
} // namespace
std::vector<CameraFramePlan> CameraFramePlanBuilder::BuildPlans(
const std::vector<CameraRenderRequest>& requests,
const RenderPipelineAsset* pipelineAsset) {
@@ -89,8 +33,6 @@ void CameraFramePlanBuilder::ConfigurePlans(
} else {
ApplyDefaultRenderPipelineAssetCameraFramePlanPolicy(plan);
}
ConfigureLegacyFullscreenStageRequests(plan);
}
}

View File

@@ -53,27 +53,20 @@ void PlanCameraFrameFullscreenStages(CameraFramePlan& plan) {
if (hasPostProcess) {
plan.SetOwnedPostProcessSequence(
SharePassSequence(std::move(postProcessSequence)));
plan.colorChain.usesGraphManagedSceneColor = true;
plan.colorChain.postProcess.source = CameraFrameColorSource::MainSceneColor;
plan.colorChain.postProcess.usesGraphManagedOutputColor = hasFinalOutput;
if (!hasFinalOutput) {
plan.postProcess.destinationSurface = plan.request.surface;
}
plan.RequestFullscreenStage(
CameraFrameStage::PostProcess,
CameraFrameColorSource::MainSceneColor,
hasFinalOutput);
}
if (hasFinalOutput) {
plan.SetOwnedFinalOutputSequence(
SharePassSequence(std::move(finalOutputSequence)));
plan.colorChain.usesGraphManagedSceneColor = true;
plan.colorChain.finalOutput.source =
plan.RequestFullscreenStage(
CameraFrameStage::FinalOutput,
hasPostProcess
? CameraFrameColorSource::PostProcessColor
: CameraFrameColorSource::MainSceneColor;
plan.finalOutput.destinationSurface = plan.request.surface;
}
if (plan.UsesGraphManagedOutputColor(CameraFrameStage::MainScene)) {
plan.ConfigureGraphManagedSceneSurface();
: CameraFrameColorSource::MainSceneColor);
}
}

View File

@@ -4444,6 +4444,49 @@ TEST(RenderPipelineHost_Test, PlansFullscreenStagesFromPipelineAssetPolicy) {
EXPECT_TRUE(plan.IsFinalOutputStageValid());
}
TEST(RenderPipelineHost_Test, DoesNotPromoteFullscreenPassRequestFieldsAfterAssetPlanning) {
RenderPassSequence postProcessPasses;
auto assetState = std::make_shared<MockPipelineAssetState>();
assetState->configureCameraFramePlan = [&postProcessPasses](
CameraFramePlan& plan) {
plan.postProcess.passes = &postProcessPasses;
};
auto allocationState = std::make_shared<MockShadowAllocationState>();
MockShadowView colorView(
allocationState,
XCEngine::RHI::ResourceViewType::RenderTarget,
XCEngine::RHI::Format::R8G8B8A8_UNorm,
XCEngine::RHI::ResourceViewDimension::Texture2D);
MockShadowView depthView(
allocationState,
XCEngine::RHI::ResourceViewType::DepthStencil,
XCEngine::RHI::Format::D24_UNorm_S8_UInt,
XCEngine::RHI::ResourceViewDimension::Texture2D);
CameraRenderRequest request = {};
request.context = CreateValidContext();
request.surface = RenderSurface(320, 180);
request.surface.SetColorAttachment(&colorView);
request.surface.SetDepthAttachment(&depthView);
RenderPipelineHost host(std::make_shared<MockPipelineAsset>(assetState));
const std::vector<CameraFramePlan> plans =
host.BuildFramePlans({ request });
ASSERT_EQ(plans.size(), 1u);
const CameraFramePlan& plan = plans[0];
EXPECT_EQ(plan.postProcess.passes, &postProcessPasses);
EXPECT_FALSE(
plan.IsFullscreenStageRequested(
CameraFrameStage::PostProcess));
EXPECT_FALSE(plan.UsesGraphManagedSceneColor());
EXPECT_EQ(
plan.ResolveStageColorSource(CameraFrameStage::PostProcess),
CameraFrameColorSource::ExplicitSurface);
}
TEST(RenderPipelineHost_Test, ForwardsPipelineAssetLifetimeAndRenderCallsToCameraRenderer) {
Scene scene("RenderPipelineHostScene");