refactor(srp): unify main-scene feature injection

This commit is contained in:
2026-04-22 02:15:03 +08:00
parent 36c4ae414b
commit 9b4a302f6a
7 changed files with 198 additions and 35 deletions

View File

@@ -0,0 +1,73 @@
# SRP / URP Main Scene Feature Injection Plan
时间2026-04-22
## 背景
当前 renderer block 主线已经基本清晰:
- `ShadowCaster`
- `DepthPrepass`
- `MainScene`
- `PostProcess`
- `FinalOutput`
`UniversalRenderer` 也已经拆出了对应 block owner。
`main scene` 这一面的 renderer feature 注入还不够干净:
1. `RenderObjectsRendererFeature` 自己手写 scene-stage 与 pass-stage 判断。
2. `BuiltinGaussianSplatRendererFeature` / `BuiltinVolumetricRendererFeature` 各自重复一套 native scene feature pass 注入逻辑。
3. 缺少统一的 main-scene feature 注入骨架,后续继续加 scene feature 时还会复制这些判断和样板。
## 本阶段目标
补齐 `main scene` feature 注入的统一 utility / controller把当前散落在 feature 里的 main-scene 注入规则收口。
目标结果:
1. `RenderObjectsRendererFeature` 走统一的 main-scene pass 注入 helper。
2. Gaussian / Volumetric 共用同一套 native scene feature controller。
3. 后续再加 main-scene renderer feature 时,有稳定的落点可复用。
## 范围
本阶段只处理 main-scene feature 注入骨架,不做:
- public renderer feature API 改造
- deferred rendering
- C++ host / RenderGraph 改造
## 实施步骤
### 1. 新增 main-scene feature 注入 utility
职责:
- 统一判断当前是否处于 main scene stage
- 统一判断 pass 是否属于当前 stage
- 统一执行 enqueue
### 2. 新增 native scene feature controller
职责:
- 持有 `NativeSceneFeaturePass`
- 统一 create / configure / enqueue
- 供 Gaussian / Volumetric 复用
### 3. 接入现有 feature
接入:
- `RenderObjectsRendererFeature`
- `BuiltinGaussianSplatRendererFeature`
- `BuiltinVolumetricRendererFeature`
## 完成标准
满足以下条件才算本阶段收口:
1. main-scene feature 注入规则不再散落在多个 feature 里重复实现。
2. Gaussian / Volumetric 共用统一 native scene feature controller。
3. `XCEditor` 编译通过old editor 冒烟通过。

View File

@@ -256,6 +256,8 @@ set(XCENGINE_RENDER_PIPELINES_UNIVERSAL_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalFinalOutputBlock.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneData.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneBlock.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalMainSceneFeatureUtility.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalNativeSceneFeatureController.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalDepthPrepassBlock.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalPostProcessBlock.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderPipeline.cs

View File

@@ -8,26 +8,23 @@ namespace XCEngine.Rendering.Universal
{
public RenderPassEvent passEvent =
RenderPassEvent.BeforeRenderingTransparents;
private NativeSceneFeaturePass m_pass;
private readonly UniversalNativeSceneFeatureController m_controller =
new UniversalNativeSceneFeatureController(
NativeSceneFeaturePassId
.BuiltinGaussianSplat);
protected override int ComputeRuntimeStateHash()
{
int hash =
base.ComputeRuntimeStateHash();
hash =
RuntimeStateHashUtility.Combine(
hash,
(int)passEvent);
return hash;
return m_controller.AppendRuntimeStateHash(
hash,
passEvent);
}
public override void Create()
{
m_pass = new NativeSceneFeaturePass(
NativeSceneFeaturePassId
.BuiltinGaussianSplat,
passEvent);
m_controller.Create(passEvent);
}
public override void AddRenderPasses(
@@ -42,9 +39,10 @@ namespace XCEngine.Rendering.Universal
}
CreateInstance();
m_pass.Configure(passEvent);
renderer.EnqueuePass(m_pass);
m_controller.EnqueuePass(
renderer,
renderingData,
passEvent);
}
}
}

View File

@@ -8,26 +8,23 @@ namespace XCEngine.Rendering.Universal
{
public RenderPassEvent passEvent =
RenderPassEvent.BeforeRenderingTransparents;
private NativeSceneFeaturePass m_pass;
private readonly UniversalNativeSceneFeatureController m_controller =
new UniversalNativeSceneFeatureController(
NativeSceneFeaturePassId
.BuiltinVolumetric);
protected override int ComputeRuntimeStateHash()
{
int hash =
base.ComputeRuntimeStateHash();
hash =
RuntimeStateHashUtility.Combine(
hash,
(int)passEvent);
return hash;
return m_controller.AppendRuntimeStateHash(
hash,
passEvent);
}
public override void Create()
{
m_pass = new NativeSceneFeaturePass(
NativeSceneFeaturePassId
.BuiltinVolumetric,
passEvent);
m_controller.Create(passEvent);
}
public override void AddRenderPasses(
@@ -42,9 +39,10 @@ namespace XCEngine.Rendering.Universal
}
CreateInstance();
m_pass.Configure(passEvent);
renderer.EnqueuePass(m_pass);
m_controller.EnqueuePass(
renderer,
renderingData,
passEvent);
}
}
}

View File

@@ -136,13 +136,10 @@ namespace XCEngine.Rendering.Universal
scenePhase,
BuildRendererListDesc(),
BuildDrawingSettings());
if (!m_pass.SupportsStage(
renderingData.stage))
{
return;
}
renderer.EnqueuePass(m_pass);
UniversalMainSceneFeatureUtility.EnqueuePass(
renderer,
renderingData,
m_pass);
}
private RendererListDesc BuildRendererListDesc()

View File

@@ -0,0 +1,42 @@
using XCEngine;
using XCEngine.Rendering;
namespace XCEngine.Rendering.Universal
{
internal static class UniversalMainSceneFeatureUtility
{
public static bool IsActive(
RenderingData renderingData)
{
return renderingData != null &&
renderingData.isMainSceneStage;
}
public static bool SupportsPass(
RenderingData renderingData,
ScriptableRenderPass renderPass)
{
return IsActive(renderingData) &&
renderPass != null &&
renderPass.SupportsStage(
renderingData.stage);
}
public static bool EnqueuePass(
ScriptableRenderer renderer,
RenderingData renderingData,
ScriptableRenderPass renderPass)
{
if (renderer == null ||
!SupportsPass(
renderingData,
renderPass))
{
return false;
}
renderer.EnqueuePass(renderPass);
return true;
}
}
}

View File

@@ -0,0 +1,53 @@
using XCEngine;
using XCEngine.Rendering;
namespace XCEngine.Rendering.Universal
{
internal sealed class UniversalNativeSceneFeatureController
{
private readonly NativeSceneFeaturePassId m_featurePassId;
private NativeSceneFeaturePass m_pass;
public UniversalNativeSceneFeatureController(
NativeSceneFeaturePassId featurePassId)
{
m_featurePassId =
featurePassId;
}
public int AppendRuntimeStateHash(
int hash,
RenderPassEvent passEvent)
{
return RuntimeStateHashUtility.Combine(
hash,
(int)passEvent);
}
public void Create(
RenderPassEvent passEvent)
{
if (m_pass == null)
{
m_pass = new NativeSceneFeaturePass(
m_featurePassId,
passEvent);
return;
}
m_pass.Configure(passEvent);
}
public void EnqueuePass(
ScriptableRenderer renderer,
RenderingData renderingData,
RenderPassEvent passEvent)
{
Create(passEvent);
UniversalMainSceneFeatureUtility.EnqueuePass(
renderer,
renderingData,
m_pass);
}
}
}