fix(rendering): derive URP stages from pass queue

This commit is contained in:
2026-04-27 13:45:50 +08:00
parent 1de1f768d3
commit 8ebbd5d633
9 changed files with 267 additions and 112 deletions

View File

@@ -35,7 +35,7 @@ Unity 兼容的公开命名、对象所有权和扩展点。
-`BuiltinForwardPipeline` 从策略中心降级为默认 native scene draw backend。它可以
继续作为 backend/fallback 实现存在,但不应决定 URP pass 顺序、feature 参与方式或
stage 策略。
-`ScriptableRenderer` 及其 active pass queue 成为 URP main-scene scheduling 的权威。
-`ScriptableRenderer` 及其 active pass queue 成为 URP stage planning 和 recording 的权威。
Opaque、skybox、transparent、depth、shadow、post 和 final-output 流程应来自 URP
blocks 和 passes。
- 保持 `DrawObjectsPass``DrawSkyboxPass` 作为 managed URP pass declarations并通过
@@ -210,6 +210,9 @@ package。
- `ScriptableRenderer` 按 stage 构建 `m_activePassQueue`。它依次调用 feature `SetupRenderPasses`、feature
`AddRenderPasses`,再调用 renderer-owned `AddRenderPasses`passes 按 `RenderPassEvent` 顺序插入,并归组到
`RendererBlocks`
- URP stage planning 以 `ScriptableRenderer` 的 active pass queue 为最终事实源。`ConfigureCameraFramePlan`
仍是兼容和高级策略 hook但它不能单独声明 shadow、depth、post 或 final-output stage没有被 pass queue
覆盖到的 side/fullscreen stage 必须在最终 plan 中清掉。
- `RendererBlock` 将 pass events 映射到 camera stagesshadow caster、depth prepass、main opaque、main
skybox、main transparent、post process 和 final output。
- `UniversalRenderer` 拥有具体 blocks
@@ -219,9 +222,9 @@ package。
implementations 包括 `RenderObjectsRendererFeature``ColorScalePostProcessRendererFeature`
`DisableDirectionalShadowRendererFeature`
- 新增 URP feature 时,保持 serializable settings、runtime hash、`Create`
`ConfigureCameraRenderRequest``ConfigureCameraFramePlan``ConfigureRenderSceneSetup`
`AddRenderPasses` 和 stage gate behavior 一致。如果 feature 需要 stage先在 planning 阶段 request
再 enqueue pass
`ConfigureCameraRenderRequest``ConfigureRenderSceneSetup``AddRenderPasses` 和 stage gate behavior 一致。
如果 feature 需要 stage必须 enqueue 一个 `RenderPassEvent` 匹配该 stage 的 pass不要只依赖
`ConfigureCameraFramePlan` 请求 stage
### Managed RenderGraph
@@ -306,7 +309,8 @@ Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。
- Managed `CommandBuffer` 和 public `SetRenderFunc` 还不会执行 deferred native command lists。
- Managed RenderGraph 当前暴露 raster authoring。Native graph 有 compute pass support但 managed compute
authoring 还未公开。
- `UniversalPostProcessBlock` 主要负责 planning 和 promotion实际 post-process work 当前来自 features/passes。
- `UniversalPostProcessBlock` 仍保留 post-process source promotion helper实际 post-process stage 由
active pass queue 中的 features/passes 声明。
- `UniversalRendererData` 和 features 是 code-created objects还不是完整 Unity 风格 serialized asset pipeline。
- 当前 shadow support 是单个 main directional shadow path没有 cascades。
- Graph compiler/executor 当前没有实现 pass culling 或 transient aliasing。
@@ -325,6 +329,8 @@ Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。
RenderGraph。
- URP 现在已有 renderer data、renderer features、renderer pass queueing、renderer blocks、renderer-index
resolution 和 per-stage recording。
- URP stage planning 已收口到 renderer active pass queue 派生的 stage manifest关闭了 feature planning hook
与实际 pass queue 分离的重复事实源。
- Public managed RenderGraph raster authoring 已存在internal fullscreen kernels 仍是 URP implementation
details。
- Public `ScriptableRenderPass.RecordRenderGraph(RenderGraph, ContextContainer)` 通过 `RenderingData`

View File

@@ -423,83 +423,6 @@ namespace Gameplay
}
}
public override void ConfigureCameraFramePlan(
ScriptableRenderPipelinePlanningContext context)
{
if (context == null)
{
return;
}
bool hasPostProcessPass = false;
bool hasFinalOutputPass = false;
for (int i = 0; i < m_passes.Length; ++i)
{
ScriptableRenderPass renderPass = m_passes[i];
if (renderPass == null)
{
continue;
}
if (renderPass.SupportsStage(
CameraFrameStage.PostProcess))
{
hasPostProcessPass = true;
}
if (renderPass.SupportsStage(
CameraFrameStage.FinalOutput))
{
hasFinalOutputPass = true;
}
}
bool needsGraphManagedPostProcessOutput =
context.HasFinalColorProcessing() ||
context.IsStageRequested(
CameraFrameStage.FinalOutput) ||
hasFinalOutputPass;
if (hasPostProcessPass)
{
if (!context.IsStageRequested(
CameraFrameStage.PostProcess))
{
context.RequestFullscreenStage(
CameraFrameStage.PostProcess,
CameraFrameColorSource.MainSceneColor,
needsGraphManagedPostProcessOutput);
}
else if (needsGraphManagedPostProcessOutput &&
context.GetStageColorSource(
CameraFrameStage.PostProcess) !=
CameraFrameColorSource.ExplicitSurface &&
!context.UsesGraphManagedOutputColor(
CameraFrameStage.PostProcess))
{
CameraFrameColorSource source =
context.GetStageColorSource(
CameraFrameStage.PostProcess);
context.ClearFullscreenStage(
CameraFrameStage.PostProcess);
context.RequestFullscreenStage(
CameraFrameStage.PostProcess,
source,
true);
}
}
if (hasFinalOutputPass &&
!context.IsStageRequested(
CameraFrameStage.FinalOutput))
{
context.RequestFullscreenStage(
CameraFrameStage.FinalOutput,
hasPostProcessPass
? CameraFrameColorSource.PostProcessColor
: CameraFrameColorSource.MainSceneColor);
}
}
}
internal class ProbeSceneRenderer : ScriptableRenderer

View File

@@ -71,13 +71,6 @@ namespace XCEngine.Rendering.Universal
BuildSettings());
}
public override void ConfigureCameraFramePlan(
ScriptableRenderPipelinePlanningContext context)
{
m_controller.ConfigureCameraFramePlan(
context);
}
public override void AddRenderPasses(
ScriptableRenderer renderer,
RenderingData renderingData)

View File

@@ -27,6 +27,27 @@ namespace XCEngine.Rendering.Universal
{
}
internal static RenderingData CreatePlanning(
CameraFrameStage stage,
int rendererIndex,
bool finalColorRequiresProcessing)
{
return new RenderingData(
stage,
rendererIndex,
CameraData.Default,
LightingData.Default,
ShadowData.Default,
EnvironmentData.Default,
finalColorRequiresProcessing
? new FinalColorData(
CreatePlanningFinalColorSettings(),
true,
false)
: FinalColorData.Default,
StageColorData.Default);
}
internal RenderingData(ScriptableRenderContext context)
: this(
context != null
@@ -116,5 +137,19 @@ namespace XCEngine.Rendering.Universal
public bool isFinalOutputStage =>
stage == CameraFrameStage.FinalOutput;
private static FinalColorSettings
CreatePlanningFinalColorSettings()
{
FinalColorSettings settings =
FinalColorSettings.CreateDefault();
settings.finalColorScale =
new Vector4(
0.5f,
0.5f,
0.5f,
1.0f);
return settings;
}
}
}

View File

@@ -7,6 +7,14 @@ namespace XCEngine.Rendering.Universal
{
public abstract class ScriptableRenderer
{
private struct PassQueueStageManifest
{
public bool shadowCaster;
public bool depthOnly;
public bool postProcess;
public bool finalOutput;
}
private readonly List<ScriptableRendererFeature> m_features =
new List<ScriptableRendererFeature>();
private readonly List<ScriptableRenderPass> m_activePassQueue =
@@ -118,6 +126,20 @@ namespace XCEngine.Rendering.Universal
ConfigureRendererFeaturesCameraFramePlan(context);
}
internal void ConfigurePassQueueCameraFramePlanInstance(
ScriptableRenderPipelinePlanningContext context)
{
if (context == null)
{
return;
}
PassQueueStageManifest manifest =
BuildPassQueueStageManifest(context);
ApplyPassQueueStageManifest(context, manifest);
ClearPassQueue();
}
internal void FinalizeCameraFramePlanInstance(
ScriptableRenderPipelinePlanningContext context)
{
@@ -282,7 +304,13 @@ namespace XCEngine.Rendering.Universal
return false;
}
switch (context.stage)
return HasRendererStageBlocks(context.stage);
}
private bool HasRendererStageBlocks(
CameraFrameStage stage)
{
switch (stage)
{
case CameraFrameStage.ShadowCaster:
return HasRendererBlock(
@@ -308,6 +336,180 @@ namespace XCEngine.Rendering.Universal
}
}
private PassQueueStageManifest BuildPassQueueStageManifest(
ScriptableRenderPipelinePlanningContext context)
{
bool finalColorRequiresProcessing =
context.HasFinalColorProcessing();
int rendererIndex = context.rendererIndex;
return new PassQueueStageManifest
{
shadowCaster =
HasPassQueueStage(
CameraFrameStage.ShadowCaster,
rendererIndex,
finalColorRequiresProcessing),
depthOnly =
HasPassQueueStage(
CameraFrameStage.DepthOnly,
rendererIndex,
finalColorRequiresProcessing),
postProcess =
HasPassQueueStage(
CameraFrameStage.PostProcess,
rendererIndex,
finalColorRequiresProcessing),
finalOutput =
HasPassQueueStage(
CameraFrameStage.FinalOutput,
rendererIndex,
finalColorRequiresProcessing)
};
}
private bool HasPassQueueStage(
CameraFrameStage stage,
int rendererIndex,
bool finalColorRequiresProcessing)
{
BuildPassQueue(
RenderingData.CreatePlanning(
stage,
rendererIndex,
finalColorRequiresProcessing));
return HasRendererStageBlocks(stage);
}
private static void ApplyPassQueueStageManifest(
ScriptableRenderPipelinePlanningContext context,
PassQueueStageManifest manifest)
{
if (manifest.shadowCaster)
{
context.RequestShadowCasterStage();
}
else
{
context.ClearShadowCasterStage();
}
if (manifest.depthOnly)
{
context.RequestCameraDepthOnlyStage();
}
else
{
context.ClearDepthOnlyStage();
}
ApplyPostProcessStageManifest(context, manifest);
ApplyFinalOutputStageManifest(context, manifest);
}
private static void ApplyPostProcessStageManifest(
ScriptableRenderPipelinePlanningContext context,
PassQueueStageManifest manifest)
{
if (!manifest.postProcess)
{
context.ClearFullscreenStage(
CameraFrameStage.PostProcess);
return;
}
bool finalOutputReadsPostProcess =
ShouldFinalOutputReadPostProcess(
context,
manifest);
if (!context.IsStageRequested(
CameraFrameStage.PostProcess))
{
context.RequestFullscreenStage(
CameraFrameStage.PostProcess,
CameraFrameColorSource.MainSceneColor,
finalOutputReadsPostProcess);
return;
}
if (finalOutputReadsPostProcess &&
!context.UsesGraphManagedOutputColor(
CameraFrameStage.PostProcess))
{
CameraFrameColorSource source =
context.GetStageColorSource(
CameraFrameStage.PostProcess);
context.ClearFullscreenStage(
CameraFrameStage.PostProcess);
context.RequestFullscreenStage(
CameraFrameStage.PostProcess,
source,
true);
}
}
private static bool ShouldFinalOutputReadPostProcess(
ScriptableRenderPipelinePlanningContext context,
PassQueueStageManifest manifest)
{
if (!manifest.finalOutput ||
!manifest.postProcess)
{
return false;
}
return !context.IsStageRequested(
CameraFrameStage.PostProcess) ||
context.GetStageColorSource(
CameraFrameStage.PostProcess) !=
CameraFrameColorSource.ExplicitSurface;
}
private static void ApplyFinalOutputStageManifest(
ScriptableRenderPipelinePlanningContext context,
PassQueueStageManifest manifest)
{
if (!manifest.finalOutput)
{
context.ClearFullscreenStage(
CameraFrameStage.FinalOutput);
return;
}
if (context.IsStageRequested(
CameraFrameStage.FinalOutput) &&
context.GetStageColorSource(
CameraFrameStage.FinalOutput) ==
CameraFrameColorSource.ExplicitSurface)
{
return;
}
context.ClearFullscreenStage(
CameraFrameStage.FinalOutput);
context.RequestFullscreenStage(
CameraFrameStage.FinalOutput,
ResolveFinalOutputSource(context));
}
private static CameraFrameColorSource ResolveFinalOutputSource(
ScriptableRenderPipelinePlanningContext context)
{
if (context == null ||
!context.IsStageRequested(
CameraFrameStage.PostProcess))
{
return CameraFrameColorSource.MainSceneColor;
}
return context.GetStageColorSource(
CameraFrameStage.PostProcess) ==
CameraFrameColorSource.ExplicitSurface
? CameraFrameColorSource.MainSceneColor
: CameraFrameColorSource.PostProcessColor;
}
protected virtual bool RecordRendererStage(
RendererRecordingContext context,
RenderGraph renderGraph,
@@ -432,8 +634,7 @@ namespace XCEngine.Rendering.Universal
private void BuildPassQueue(
RenderingData renderingData)
{
m_activePassQueue.Clear();
m_rendererBlocks.Clear();
ClearPassQueue();
m_isBuildingPassQueue = true;
m_passQueueStage =
renderingData != null
@@ -469,6 +670,12 @@ namespace XCEngine.Rendering.Universal
}
}
private void ClearPassQueue()
{
m_activePassQueue.Clear();
m_rendererBlocks.Clear();
}
private void FinishCameraStackRendering()
{
for (int i = 0; i < m_activePassQueue.Count; ++i)

View File

@@ -110,6 +110,12 @@ namespace XCEngine.Rendering.Universal
renderer.FinalizeCameraFramePlanInstance(
context);
}
if (renderer != null)
{
renderer.ConfigurePassQueueCameraFramePlanInstance(
context);
}
}
internal bool ConfigureRenderSceneSetupInstance(

View File

@@ -42,7 +42,7 @@ namespace XCEngine.Rendering.Universal
return;
}
if (!renderingData.isFinalOutputStage &&
if (!renderingData.isFinalOutputStage ||
!renderingData.finalColorData.requiresProcessing)
{
return;

View File

@@ -78,21 +78,6 @@ namespace ProjectScripts
new ProjectPostProcessColorScalePass();
}
public override void ConfigureCameraFramePlan(
ScriptableRenderPipelinePlanningContext context)
{
if (context == null ||
context.IsStageRequested(
CameraFrameStage.PostProcess))
{
return;
}
context.RequestFullscreenStage(
CameraFrameStage.PostProcess,
CameraFrameColorSource.MainSceneColor);
}
public override void AddRenderPasses(
ScriptableRenderer renderer,
RenderingData renderingData)

View File

@@ -4614,7 +4614,7 @@ TEST_F(
TEST_F(
MonoScriptRuntimeTest,
ManagedRenderPipelineAssetPlansFullscreenStagesFromPipelineStageSupport) {
ManagedRenderPipelineAssetPlansFullscreenStagesFromRendererPassQueue) {
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
"GameScripts",
"Gameplay",
@@ -4743,7 +4743,7 @@ TEST_F(
TEST_F(
MonoScriptRuntimeTest,
ManagedRendererFeatureConfiguresCameraFramePlanThroughPlanningContext) {
ManagedRendererFeaturePlanningHookAloneDoesNotDeclareFullscreenStage) {
Scene* runtimeScene =
CreateScene("ManagedFeatureFramePlanningScene");
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
@@ -4796,13 +4796,13 @@ TEST_F(
ASSERT_EQ(plans.size(), 1u);
const XCEngine::Rendering::CameraFramePlan& plan = plans[0];
EXPECT_TRUE(
EXPECT_FALSE(
plan.IsFullscreenStageRequested(
XCEngine::Rendering::CameraFrameStage::PostProcess));
EXPECT_EQ(
plan.ResolveStageColorSource(
XCEngine::Rendering::CameraFrameStage::PostProcess),
XCEngine::Rendering::CameraFrameColorSource::MainSceneColor);
XCEngine::Rendering::CameraFrameColorSource::ExplicitSurface);
EXPECT_FALSE(
plan.UsesGraphManagedOutputColor(
XCEngine::Rendering::CameraFrameStage::PostProcess));