diff --git a/engine/include/XCEngine/Rendering/AGENTS.md b/engine/include/XCEngine/Rendering/AGENTS.md index f5bda439..3b28615d 100644 --- a/engine/include/XCEngine/Rendering/AGENTS.md +++ b/engine/include/XCEngine/Rendering/AGENTS.md @@ -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 stages:shadow 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`、 diff --git a/managed/GameScripts/RenderPipelineApiProbe.cs b/managed/GameScripts/RenderPipelineApiProbe.cs index 70792b1b..4e978a2a 100644 --- a/managed/GameScripts/RenderPipelineApiProbe.cs +++ b/managed/GameScripts/RenderPipelineApiProbe.cs @@ -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 diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs index c167d94d..5f5c3a0a 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs @@ -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) diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderingData.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderingData.cs index 265e3bdb..52736610 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderingData.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/RenderingData.cs @@ -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; + } } } diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs index 5e7fbc28..628f7de7 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs @@ -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 m_features = new List(); private readonly List 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) diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs index f62eb335..6d10c002 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRendererData.cs @@ -110,6 +110,12 @@ namespace XCEngine.Rendering.Universal renderer.FinalizeCameraFramePlanInstance( context); } + + if (renderer != null) + { + renderer.ConfigurePassQueueCameraFramePlanInstance( + context); + } } internal bool ConfigureRenderSceneSetupInstance( diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalFinalOutputBlock.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalFinalOutputBlock.cs index 9ce5df64..8add3d2d 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalFinalOutputBlock.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalFinalOutputBlock.cs @@ -42,7 +42,7 @@ namespace XCEngine.Rendering.Universal return; } - if (!renderingData.isFinalOutputStage && + if (!renderingData.isFinalOutputStage || !renderingData.finalColorData.requiresProcessing) { return; diff --git a/project/Assets/Scripts/ProjectRenderPipelineProbe.cs b/project/Assets/Scripts/ProjectRenderPipelineProbe.cs index 4e29b65f..eff468a6 100644 --- a/project/Assets/Scripts/ProjectRenderPipelineProbe.cs +++ b/project/Assets/Scripts/ProjectRenderPipelineProbe.cs @@ -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) diff --git a/tests/scripting/test_mono_script_runtime.cpp b/tests/scripting/test_mono_script_runtime.cpp index e66259e1..40d27705 100644 --- a/tests/scripting/test_mono_script_runtime.cpp +++ b/tests/scripting/test_mono_script_runtime.cpp @@ -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));