refactor(srp): infer fullscreen stages from renderer pass queue
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
# SRP Unity-Style URP Fullscreen Stage Inference Plan
|
||||
|
||||
日期:2026-04-21
|
||||
|
||||
## 阶段目标
|
||||
|
||||
继续沿着 Unity 的 `ScriptableRenderer + RendererFeature + RenderPassEvent` 模型收敛,解决当前 fullscreen stage 仍然需要 feature 手写 `ConfigureCameraFramePlan(...)` 的问题。
|
||||
|
||||
本阶段的核心目标是:
|
||||
|
||||
- 让 `ScriptableRenderer` 能基于 pass queue 自动推导 `PostProcess / FinalOutput` 这类 fullscreen stage 需求
|
||||
- 让 `UniversalRenderer` 在 feature 参与之后再完成 final output 的依赖收口
|
||||
- 减少 feature 作者同时维护 “AddRenderPasses + ConfigureCameraFramePlan” 两套逻辑的重复负担
|
||||
|
||||
## 当前问题
|
||||
|
||||
当前结构里,feature 如果要往 `PostProcess` 塞一个 pass,通常必须同时做两件事:
|
||||
|
||||
1. 在 `AddRenderPasses(...)` 里 enqueue pass
|
||||
2. 在 `ConfigureCameraFramePlan(...)` 里手动请求 `PostProcess`
|
||||
|
||||
这不符合 Unity 的主思路。Unity 里用户主要写的是:
|
||||
|
||||
- `RendererFeature`
|
||||
- `ScriptableRenderPass`
|
||||
- `renderPassEvent`
|
||||
|
||||
而不是再额外手工参与底层 stage existence planning。
|
||||
|
||||
当前这套方式还有一个实际问题:
|
||||
|
||||
1. `UniversalRenderer` 现在已经接管了 `FinalOutput`
|
||||
2. 但 feature 对 `PostProcess` 的请求发生在 renderer 默认规划之后
|
||||
3. 这会让 `FinalOutput` 难以及时感知 feature 注入出来的 `PostProcess` 依赖
|
||||
|
||||
## 本阶段实施内容
|
||||
|
||||
### 1. 增加 renderer 规划收尾阶段
|
||||
|
||||
在 renderer 规划流程中补一个 finalize/fixup 阶段:
|
||||
|
||||
1. renderer 先做 core default planning
|
||||
2. feature 再做显式 override
|
||||
3. renderer 最后基于 pass queue 和当前 stage 状态做依赖收口
|
||||
|
||||
这样 renderer 才能在 feature 参与后,看到最终的 fullscreen stage 需求。
|
||||
|
||||
### 2. 基于 pass queue 自动推导 fullscreen stage
|
||||
|
||||
让 `ScriptableRenderer` 使用已经 enqueue 的 pass 队列自动推导:
|
||||
|
||||
1. 是否需要 `PostProcess`
|
||||
2. 是否需要 `FinalOutput`
|
||||
|
||||
优先先做 fullscreen stage,不一口气泛化到所有 stage。
|
||||
|
||||
### 3. 清理 feature 侧重复请求
|
||||
|
||||
拿 `ColorScalePostProcessRendererFeature` 作为第一批清理对象:
|
||||
|
||||
1. 保留 `AddRenderPasses(...)`
|
||||
2. 去掉手写的 `ConfigureCameraFramePlan(...)`
|
||||
3. 改由 renderer 自动根据 pass queue 推导 `PostProcess`
|
||||
|
||||
### 4. 让 UniversalRenderer 在 finalize 阶段收 final output
|
||||
|
||||
`UniversalRenderer` 的 `FinalOutput` 依赖不再在 feature 之前就定死,而是在 fullscreen stage 自动推导之后再做:
|
||||
|
||||
1. 若 feature 导致 `PostProcess` 存在
|
||||
2. 且 pipeline 需要 final color processing
|
||||
3. 则 `FinalOutput` 应该正确依赖 `PostProcessColor`
|
||||
|
||||
## 本阶段不做的事情
|
||||
|
||||
1. 不做 deferred renderer
|
||||
2. 不做 GBuffer
|
||||
3. 不做 shadow pass managed 化
|
||||
4. 不重写完整的 Unity RenderPassEvent 大表
|
||||
5. 不一口气移除所有 `ConfigureCameraFramePlan(...)`
|
||||
|
||||
## 完成标准
|
||||
|
||||
1. renderer 规划流程具备 finalize/fixup 阶段
|
||||
2. `ScriptableRenderer` 能基于 pass queue 自动推导 `PostProcess / FinalOutput`
|
||||
3. `ColorScalePostProcessRendererFeature` 不再手写请求 `PostProcess`
|
||||
4. `UniversalRenderer` 在 finalize 阶段正确收口 final output 依赖
|
||||
5. 旧版 `XCEditor` Debug 编译通过
|
||||
6. 旧版编辑器冒烟 10 秒以上,并在新的 `editor.log` 中出现 `SceneReady`
|
||||
|
||||
## 下一阶段前置条件
|
||||
|
||||
只有这一阶段收口后,再进入下一个更接近 Unity URP 的阶段:
|
||||
|
||||
- `Renderer Block / Event Semantics Expansion`
|
||||
|
||||
也就是进一步把 renderer 的 block 边界和 `RenderPassEvent` 语义扩全,为未来的不同 renderer variant 和更完整的 URP 风格扩展点做准备。
|
||||
@@ -54,22 +54,6 @@ namespace XCEngine.Rendering.Universal
|
||||
new ColorScalePostProcessPass(this);
|
||||
}
|
||||
|
||||
public override void ConfigureCameraFramePlan(
|
||||
ScriptableRenderPipelinePlanningContext context)
|
||||
{
|
||||
if (context == null ||
|
||||
context.IsStageRequested(
|
||||
CameraFrameStage.PostProcess))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.RequestFullscreenStage(
|
||||
CameraFrameStage.PostProcess,
|
||||
CameraFrameColorSource.MainSceneColor,
|
||||
context.HasFinalColorProcessing());
|
||||
}
|
||||
|
||||
public override void AddRenderPasses(
|
||||
ScriptableRenderer renderer,
|
||||
RenderingData renderingData)
|
||||
|
||||
@@ -95,6 +95,12 @@ namespace XCEngine.Rendering.Universal
|
||||
ConfigureCameraFramePlan(context);
|
||||
}
|
||||
|
||||
internal void FinalizeCameraFramePlanInstance(
|
||||
ScriptableRenderPipelinePlanningContext context)
|
||||
{
|
||||
FinalizeCameraFramePlan(context);
|
||||
}
|
||||
|
||||
internal bool RecordRendererInstance(
|
||||
RendererRecordingContext context)
|
||||
{
|
||||
@@ -181,6 +187,18 @@ namespace XCEngine.Rendering.Universal
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void FinalizeCameraFramePlan(
|
||||
ScriptableRenderPipelinePlanningContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyInferredFullscreenStageRequests(
|
||||
context);
|
||||
}
|
||||
|
||||
private void BuildPassQueue(
|
||||
RenderingData renderingData)
|
||||
{
|
||||
@@ -205,6 +223,111 @@ namespace XCEngine.Rendering.Universal
|
||||
protected virtual void ReleaseRuntimeResources()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual RenderingData CreatePlanningRenderingData(
|
||||
ScriptableRenderPipelinePlanningContext context)
|
||||
{
|
||||
return context != null
|
||||
? new RenderingData(
|
||||
CameraFrameStage.MainScene,
|
||||
context.rendererIndex)
|
||||
: null;
|
||||
}
|
||||
|
||||
private void ApplyInferredFullscreenStageRequests(
|
||||
ScriptableRenderPipelinePlanningContext context)
|
||||
{
|
||||
RenderingData planningData =
|
||||
CreatePlanningRenderingData(context);
|
||||
if (planningData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BuildPassQueue(planningData);
|
||||
|
||||
bool hasPostProcessPass =
|
||||
HasQueuedPassForStage(
|
||||
CameraFrameStage.PostProcess);
|
||||
bool hasFinalOutputPass =
|
||||
HasQueuedPassForStage(
|
||||
CameraFrameStage.FinalOutput);
|
||||
bool needsFinalOutputDependency =
|
||||
context.HasFinalColorProcessing() ||
|
||||
context.IsStageRequested(
|
||||
CameraFrameStage.FinalOutput) ||
|
||||
hasFinalOutputPass;
|
||||
|
||||
if (hasPostProcessPass)
|
||||
{
|
||||
EnsureInferredPostProcessStage(
|
||||
context,
|
||||
needsFinalOutputDependency);
|
||||
}
|
||||
|
||||
if (hasFinalOutputPass &&
|
||||
!context.IsStageRequested(
|
||||
CameraFrameStage.FinalOutput))
|
||||
{
|
||||
context.RequestFullscreenStage(
|
||||
CameraFrameStage.FinalOutput,
|
||||
context.IsStageRequested(
|
||||
CameraFrameStage.PostProcess)
|
||||
? CameraFrameColorSource.PostProcessColor
|
||||
: CameraFrameColorSource.MainSceneColor);
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasQueuedPassForStage(
|
||||
CameraFrameStage stage)
|
||||
{
|
||||
for (int i = 0; i < m_activePassQueue.Count; ++i)
|
||||
{
|
||||
ScriptableRenderPass renderPass =
|
||||
m_activePassQueue[i];
|
||||
if (renderPass != null &&
|
||||
renderPass.SupportsStage(stage))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void EnsureInferredPostProcessStage(
|
||||
ScriptableRenderPipelinePlanningContext context,
|
||||
bool needsGraphManagedOutputColor)
|
||||
{
|
||||
if (!context.IsStageRequested(
|
||||
CameraFrameStage.PostProcess))
|
||||
{
|
||||
context.RequestFullscreenStage(
|
||||
CameraFrameStage.PostProcess,
|
||||
CameraFrameColorSource.MainSceneColor,
|
||||
needsGraphManagedOutputColor);
|
||||
return;
|
||||
}
|
||||
|
||||
CameraFrameColorSource source =
|
||||
context.GetStageColorSource(
|
||||
CameraFrameStage.PostProcess);
|
||||
if (!needsGraphManagedOutputColor ||
|
||||
source ==
|
||||
CameraFrameColorSource.ExplicitSurface ||
|
||||
context.UsesGraphManagedOutputColor(
|
||||
CameraFrameStage.PostProcess))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.ClearFullscreenStage(
|
||||
CameraFrameStage.PostProcess);
|
||||
context.RequestFullscreenStage(
|
||||
CameraFrameStage.PostProcess,
|
||||
source,
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,6 +141,12 @@ namespace XCEngine.Rendering.Universal
|
||||
rendererFeature.ConfigureCameraFramePlan(
|
||||
context);
|
||||
}
|
||||
|
||||
if (renderer != null)
|
||||
{
|
||||
renderer.FinalizeCameraFramePlanInstance(
|
||||
context);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual ScriptableRenderer CreateRenderer()
|
||||
|
||||
@@ -39,6 +39,17 @@ namespace XCEngine.Rendering.Universal
|
||||
}
|
||||
|
||||
ConfigureShadowCasterStage(context);
|
||||
}
|
||||
|
||||
protected override void FinalizeCameraFramePlan(
|
||||
ScriptableRenderPipelinePlanningContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.FinalizeCameraFramePlan(context);
|
||||
ConfigureFinalOutputStage(context);
|
||||
}
|
||||
|
||||
@@ -117,13 +128,14 @@ namespace XCEngine.Rendering.Universal
|
||||
private static void ConfigureFinalOutputStage(
|
||||
ScriptableRenderPipelinePlanningContext context)
|
||||
{
|
||||
context.ClearFullscreenStage(
|
||||
CameraFrameStage.FinalOutput);
|
||||
if (!context.HasFinalColorProcessing())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.ClearFullscreenStage(
|
||||
CameraFrameStage.FinalOutput);
|
||||
|
||||
CameraFrameColorSource finalOutputSource =
|
||||
CameraFrameColorSource.MainSceneColor;
|
||||
if (context.IsStageRequested(
|
||||
|
||||
Reference in New Issue
Block a user