From 4d587c5d0beff33fdd6f18371a9fee938298526c Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Tue, 21 Apr 2026 01:54:02 +0800 Subject: [PATCH] refactor(srp): move builtin scene feature ownership into renderer features --- ...anagedOwnership计划_完成归档_2026-04-21.md | 100 ++++++++++++++++++ .../Rendering/Pipelines/NativeSceneRecorder.h | 1 + .../Rendering/SceneRenderFeatureHost.h | 4 + .../Pipelines/NativeSceneRecorder.cpp | 38 +++++++ .../src/Rendering/SceneRenderFeatureHost.cpp | 88 +++++++++++++++ .../src/Scripting/Mono/MonoScriptRuntime.cpp | 26 +++++ managed/CMakeLists.txt | 3 + .../BuiltinGaussianSplatRendererFeature.cs | 39 +++++++ .../BuiltinVolumetricRendererFeature.cs | 39 +++++++ .../Universal/NativeSceneFeaturePass.cs | 37 +++++++ .../Rendering/Universal/UniversalRenderer.cs | 54 ---------- .../Universal/UniversalRendererData.cs | 12 ++- managed/XCEngine.ScriptCore/InternalCalls.cs | 6 ++ .../Rendering/Core/ScriptableRenderContext.cs | 11 ++ 14 files changed, 403 insertions(+), 55 deletions(-) create mode 100644 docs/used/SRP_UniversalBuiltinSceneFeaturesManagedOwnership计划_完成归档_2026-04-21.md create mode 100644 managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinGaussianSplatRendererFeature.cs create mode 100644 managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinVolumetricRendererFeature.cs create mode 100644 managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/NativeSceneFeaturePass.cs diff --git a/docs/used/SRP_UniversalBuiltinSceneFeaturesManagedOwnership计划_完成归档_2026-04-21.md b/docs/used/SRP_UniversalBuiltinSceneFeaturesManagedOwnership计划_完成归档_2026-04-21.md new file mode 100644 index 00000000..51be7ad3 --- /dev/null +++ b/docs/used/SRP_UniversalBuiltinSceneFeaturesManagedOwnership计划_完成归档_2026-04-21.md @@ -0,0 +1,100 @@ +# SRP Universal Builtin Scene Features Managed Ownership 计划 2026-04-21 + +## 1. 阶段目标 + +把当前 `GaussianSplat` / `Volumetric` 这类 main-scene 高层效果从 +“`BuiltinForwardPipeline` 在 C++ 注入点里自动持有并自动执行”的状态, +收成更接近 Unity URP 的 ownership: + +1. `UniversalRenderer` 不再依赖 generic native injection pass 来偷偷执行这些高层效果 +2. 这些效果改成由 managed `ScriptableRendererFeature` 显式挂到 `UniversalRendererData` +3. C++ 仍然保留底层 native pass 实现,但调度权回到 URP feature 层 + +## 2. 当前问题 + +当前最不 Unity-like 的点不是 `DrawRenderers` 主链,而是 builtin scene feature 的 ownership: + +1. `BuiltinForwardSceneSetup` 会直接把 `BuiltinGaussianSplatPass` 和 `BuiltinVolumetricPass` + 注册到 C++ `SceneRenderFeatureHost` +2. `UniversalRenderer` 里仍然有 `UniversalSceneInjectionPass` + 去调用 `RecordSceneInjectionPoint(BeforeTransparent)` +3. 结果是: + - `GaussianSplat` / `Volumetric` 虽然表现上跟着 URP 主线走 + - 但真正的 feature ownership 仍然在 C++ automatic injection,而不是 C# renderer feature + +这会带来三个问题: + +1. 用户无法像 Unity 一样从 renderer feature 层显式打开/关闭这些效果 +2. `UniversalRenderer` 仍然混着“显式 pass 调度”和“隐式 C++ 注入”两套模型 +3. 后续继续把阴影、更多高层特性收进 URP 时,会越来越难收口 + +## 3. 本阶段原则 + +这一步只解决 ownership,不急着把底层 GPU 实现也搬到 C#: + +1. 调度权在 managed `ScriptableRendererFeature` +2. native builtin pass 先保留为 backend implementation +3. `ScriptableRenderContext` 提供显式录制 named native scene feature pass 的桥 +4. `UniversalRenderer` 回到 Unity 风格: + - builtin scene draw pass 显式排队 + - feature pass 由 renderer feature 显式排队 + - 不再靠 generic injection pass 偷偷执行高层效果 + +## 4. 本阶段范围 + +包括: + +1. `SceneRenderFeatureHost` 增加按名字录制单个 feature pass 的入口 +2. `NativeSceneRecorder` / `ScriptableRenderContext` 打通对应 managed/native bridge +3. 新增 managed `NativeSceneFeaturePass` +4. 新增 `BuiltinGaussianSplatRendererFeature` +5. 新增 `BuiltinVolumetricRendererFeature` +6. `UniversalRenderer` 移除 generic injection pass 依赖 +7. `UniversalRendererData` 默认挂上上述 builtin managed features +8. 重新编译 `XCEditor` +9. 运行旧版 `editor/bin/Debug/XCEngine.exe` 冒烟至少 10 秒 + +不包括: + +1. 把 `GaussianSplat` / `Volumetric` 的底层 GPU 实现本身改写成纯 C# +2. 阴影系统迁移到 managed feature +3. deferred renderer +4. renderer inspector/editor UI + +## 5. 实施步骤 + +### Step 1:给 managed 侧一个显式录制 native scene feature 的桥 + +做这几件事: + +1. `SceneRenderFeatureHost` 支持按 feature pass 名称录制单个 pass +2. `NativeSceneRecorder` 暴露对应入口 +3. `ScriptableRenderContext` 增加 `RecordNativeSceneFeaturePass(string featurePassName)` + +### Step 2:把 UniversalRenderer 的 implicit injection 改成 explicit managed feature + +做这几件事: + +1. 去掉 `UniversalRenderer` 里当前为了 C++ feature host 服务的 generic injection pass +2. 新增 managed `NativeSceneFeaturePass` +3. 用 `BuiltinGaussianSplatRendererFeature` + / `BuiltinVolumetricRendererFeature` + 显式把对应 native builtin pass 挂到 `BeforeRenderingTransparents` + +### Step 3:把默认 ownership 切到 UniversalRendererData + +要求: + +1. 默认行为不能比当前少功能 +2. 用户应该可以像 Unity 一样从 `rendererFeatures` 层决定是否保留这些效果 +3. 效果排序由 `RenderPassEvent` 决定,而不是由 C++ implicit injection 决定 + +## 6. 验收标准 + +本阶段收口后必须满足: + +1. `UniversalRenderer` 不再依赖 generic `RecordSceneInjectionPoint` 来执行 `GaussianSplat` / `Volumetric` +2. `GaussianSplat` / `Volumetric` 默认通过 managed renderer feature 挂载 +3. native builtin pass 仍然可以正常执行,不丢当前功能 +4. `XCEditor` 编译通过 +5. 旧版 editor 冒烟通过 diff --git a/engine/include/XCEngine/Rendering/Pipelines/NativeSceneRecorder.h b/engine/include/XCEngine/Rendering/Pipelines/NativeSceneRecorder.h index ef50d132..a9c9f2ce 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/NativeSceneRecorder.h +++ b/engine/include/XCEngine/Rendering/Pipelines/NativeSceneRecorder.h @@ -30,6 +30,7 @@ public: bool RecordScenePhase(ScenePhase scenePhase); bool RecordSceneDrawSettings(const DrawSettings& drawSettings); bool RecordInjectionPoint(SceneRenderInjectionPoint injectionPoint); + bool RecordFeaturePass(const Containers::String& featurePassName); private: SceneRenderFeaturePassBeginCallback BuildBeginRecordedPassCallback( diff --git a/engine/include/XCEngine/Rendering/SceneRenderFeatureHost.h b/engine/include/XCEngine/Rendering/SceneRenderFeatureHost.h index fcb80ed7..4deebf1a 100644 --- a/engine/include/XCEngine/Rendering/SceneRenderFeatureHost.h +++ b/engine/include/XCEngine/Rendering/SceneRenderFeatureHost.h @@ -21,6 +21,10 @@ public: const SceneRenderFeaturePassRenderGraphContext& context, SceneRenderInjectionPoint injectionPoint, bool* recordedAnyPass = nullptr) const; + bool RecordFeaturePass( + const SceneRenderFeaturePassRenderGraphContext& context, + const Containers::String& featurePassName, + bool* recordedPass = nullptr) const; bool Execute( const FrameExecutionContext& executionContext, SceneRenderInjectionPoint injectionPoint) const; diff --git a/engine/src/Rendering/Pipelines/NativeSceneRecorder.cpp b/engine/src/Rendering/Pipelines/NativeSceneRecorder.cpp index 508ec923..95ec441f 100644 --- a/engine/src/Rendering/Pipelines/NativeSceneRecorder.cpp +++ b/engine/src/Rendering/Pipelines/NativeSceneRecorder.cpp @@ -264,6 +264,44 @@ bool NativeSceneRecorder::RecordInjectionPoint( return true; } +bool NativeSceneRecorder::RecordFeaturePass( + const Containers::String& featurePassName) { + if (m_context.stage != CameraFrameStage::MainScene || + featurePassName.Empty()) { + return false; + } + + const RenderPipelineStageRenderGraphContext graphContext = + BuildGraphContext(); + const SceneRenderFeaturePassBeginCallback beginRecordedPass = + BuildBeginRecordedPassCallback(graphContext.executionSucceeded); + const SceneRenderFeaturePassEndCallback endRecordedPass = + BuildEndRecordedPassCallback(); + SceneRenderFeatureHost* const featureHost = + m_sceneRenderer.GetFeatureHost(); + if (featureHost == nullptr) { + return false; + } + + bool recordedPass = false; + if (!featureHost->RecordFeaturePass( + BuildSceneRenderFeaturePassRenderGraphContext( + BuildRenderGraphRecordingContext( + graphContext), + m_clearAttachments, + beginRecordedPass, + endRecordedPass), + featurePassName, + &recordedPass)) { + return false; + } + + if (recordedPass) { + m_clearAttachments = false; + } + return true; +} + RenderPipelineStageRenderGraphContext NativeSceneRecorder::BuildGraphContext() const { const RenderGraphRecordingContext baseRecordingContext = diff --git a/engine/src/Rendering/SceneRenderFeatureHost.cpp b/engine/src/Rendering/SceneRenderFeatureHost.cpp index 8f7675d8..8dfd527d 100644 --- a/engine/src/Rendering/SceneRenderFeatureHost.cpp +++ b/engine/src/Rendering/SceneRenderFeatureHost.cpp @@ -29,6 +29,33 @@ Containers::String BuildFeatureGraphPassName( return Containers::String(name.c_str()); } +Containers::String BuildNamedFeatureGraphPassName( + const Containers::String& basePassName, + const SceneRenderFeaturePass& featurePass, + size_t featureIndex) { + std::string name = basePassName.CStr(); + if (!name.empty()) { + name += '.'; + } + + name += "Feature"; + name += '.'; + name += featurePass.GetName() != nullptr + ? featurePass.GetName() + : "FeaturePass"; + name += '.'; + name += std::to_string(featureIndex); + return Containers::String(name.c_str()); +} + +bool MatchesFeaturePassName( + const SceneRenderFeaturePass& featurePass, + const Containers::String& featurePassName) { + const char* const passName = featurePass.GetName(); + return passName != nullptr && + featurePassName == Containers::String(passName); +} + } // namespace void SceneRenderFeatureHost::AddFeaturePass(std::unique_ptr featurePass) { @@ -151,6 +178,67 @@ bool SceneRenderFeatureHost::Record( return true; } +bool SceneRenderFeatureHost::RecordFeaturePass( + const SceneRenderFeaturePassRenderGraphContext& context, + const Containers::String& featurePassName, + bool* recordedPass) const { + if (recordedPass != nullptr) { + *recordedPass = false; + } + + if (featurePassName.Empty()) { + return false; + } + + for (size_t featureIndex = 0u; featureIndex < m_featurePasses.size(); + ++featureIndex) { + const std::unique_ptr& featurePassOwner = + m_featurePasses[featureIndex]; + SceneRenderFeaturePass* const featurePass = featurePassOwner.get(); + if (featurePass == nullptr || + !MatchesFeaturePassName(*featurePass, featurePassName)) { + continue; + } + + if (!featurePass->IsActive(context.sceneData)) { + return true; + } + + const SceneRenderFeaturePassRenderGraphContext featureContext = + CloneSceneRenderFeaturePassRenderGraphContext( + context, + BuildNamedFeatureGraphPassName( + context.passName, + *featurePass, + featureIndex), + context.clearAttachments); + if (!featurePass->RecordRenderGraph(featureContext)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String( + "SceneRenderFeatureHost record failed for feature pass '") + + featurePassName + + "': " + + featurePass->GetName()) + .CStr()); + return false; + } + + if (recordedPass != nullptr) { + *recordedPass = true; + } + return true; + } + + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String( + "SceneRenderFeatureHost could not resolve feature pass: ") + + featurePassName) + .CStr()); + return false; +} + bool SceneRenderFeatureHost::Execute( const FrameExecutionContext& executionContext, SceneRenderInjectionPoint injectionPoint) const { diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index f00a115c..d72b7341 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -4937,6 +4937,31 @@ InternalCall_Rendering_ScriptableRenderContext_RecordSceneInjectionPoint( : 0; } +mono_bool +InternalCall_Rendering_ScriptableRenderContext_RecordNativeSceneFeaturePass( + uint64_t nativeHandle, + MonoString* featurePassName) { + ManagedScriptableRenderContextState* const state = + FindManagedScriptableRenderContextState(nativeHandle); + if (state == nullptr || + state->graphContext == nullptr || + state->sceneRecorder == nullptr || + state->stage != Rendering::CameraFrameStage::MainScene) { + return 0; + } + + const std::string featurePassNameUtf8 = + MonoStringToUtf8(featurePassName); + if (featurePassNameUtf8.empty()) { + return 0; + } + + return state->sceneRecorder->RecordFeaturePass( + Containers::String(featurePassNameUtf8.c_str())) + ? 1 + : 0; +} + mono_bool InternalCall_Rendering_ScriptableRenderContext_DrawRenderersByDesc( uint64_t nativeHandle, @@ -5392,6 +5417,7 @@ void RegisterInternalCalls() { mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordScenePhase", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RecordScenePhase)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_DrawRenderersByDesc", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_DrawRenderersByDesc)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordSceneInjectionPoint", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RecordSceneInjectionPoint)); + mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderContext_RecordNativeSceneFeaturePass", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderContext_RecordNativeSceneFeaturePass)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_GetRendererIndex", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_GetRendererIndex)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_IsStageRequested", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_IsStageRequested)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_GetStageColorSource", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_GetStageColorSource)); diff --git a/managed/CMakeLists.txt b/managed/CMakeLists.txt index a5b35fd3..3d1e9485 100644 --- a/managed/CMakeLists.txt +++ b/managed/CMakeLists.txt @@ -209,11 +209,14 @@ set(XCENGINE_RENDER_PIPELINES_UNIVERSAL_SOURCES # Universal renderer package ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/CameraData.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/ColorScalePostProcessRendererFeature.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinGaussianSplatRendererFeature.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinVolumetricRendererFeature.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/DirectionalLightData.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/DirectionalShadowData.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/DisableDirectionalShadowRendererFeature.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/DrawObjectsPass.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/DrawSkyboxPass.cs + ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/NativeSceneFeaturePass.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/EnvironmentData.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/FinalColorData.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.RenderPipelines.Universal/Rendering/Universal/LightingData.cs diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinGaussianSplatRendererFeature.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinGaussianSplatRendererFeature.cs new file mode 100644 index 00000000..bc5b2295 --- /dev/null +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinGaussianSplatRendererFeature.cs @@ -0,0 +1,39 @@ +using XCEngine; +using XCEngine.Rendering; + +namespace XCEngine.Rendering.Universal +{ + public sealed class BuiltinGaussianSplatRendererFeature + : ScriptableRendererFeature + { + public RenderPassEvent passEvent = + RenderPassEvent.BeforeRenderingTransparents; + + private NativeSceneFeaturePass m_pass; + + public override void Create() + { + m_pass = new NativeSceneFeaturePass( + "BuiltinGaussianSplatPass", + passEvent); + } + + public override void AddRenderPasses( + ScriptableRenderer renderer, + RenderingData renderingData) + { + if (renderer == null) + { + return; + } + + if (m_pass == null) + { + Create(); + } + + m_pass.Configure(passEvent); + renderer.EnqueuePass(m_pass); + } + } +} diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinVolumetricRendererFeature.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinVolumetricRendererFeature.cs new file mode 100644 index 00000000..426d6ed7 --- /dev/null +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/BuiltinVolumetricRendererFeature.cs @@ -0,0 +1,39 @@ +using XCEngine; +using XCEngine.Rendering; + +namespace XCEngine.Rendering.Universal +{ + public sealed class BuiltinVolumetricRendererFeature + : ScriptableRendererFeature + { + public RenderPassEvent passEvent = + RenderPassEvent.BeforeRenderingTransparents; + + private NativeSceneFeaturePass m_pass; + + public override void Create() + { + m_pass = new NativeSceneFeaturePass( + "BuiltinVolumetricPass", + passEvent); + } + + public override void AddRenderPasses( + ScriptableRenderer renderer, + RenderingData renderingData) + { + if (renderer == null) + { + return; + } + + if (m_pass == null) + { + Create(); + } + + m_pass.Configure(passEvent); + renderer.EnqueuePass(m_pass); + } + } +} diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/NativeSceneFeaturePass.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/NativeSceneFeaturePass.cs new file mode 100644 index 00000000..60857aaa --- /dev/null +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/NativeSceneFeaturePass.cs @@ -0,0 +1,37 @@ +using XCEngine; +using XCEngine.Rendering; + +namespace XCEngine.Rendering.Universal +{ + internal sealed class NativeSceneFeaturePass + : ScriptableRenderPass + { + private readonly string m_featurePassName; + + public NativeSceneFeaturePass( + string featurePassName, + RenderPassEvent passEvent) + { + m_featurePassName = + featurePassName ?? string.Empty; + renderPassEvent = passEvent; + } + + public void Configure( + RenderPassEvent passEvent) + { + renderPassEvent = passEvent; + } + + protected override bool RecordRenderGraph( + ScriptableRenderContext context, + RenderingData renderingData) + { + return context != null && + renderingData != null && + renderingData.isMainSceneStage && + context.RecordNativeSceneFeaturePass( + m_featurePassName); + } + } +} diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs index d9cacd97..5f0a5e83 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs @@ -3,71 +3,23 @@ using XCEngine.Rendering; namespace XCEngine.Rendering.Universal { - internal sealed class UniversalSceneInjectionPass : ScriptableRenderPass - { - private readonly SceneRenderInjectionPoint m_injectionPoint; - - public UniversalSceneInjectionPass( - RenderPassEvent passEvent, - SceneRenderInjectionPoint injectionPoint) - { - renderPassEvent = passEvent; - m_injectionPoint = injectionPoint; - } - - protected override bool RecordRenderGraph( - ScriptableRenderContext context, - RenderingData renderingData) - { - return context != null && - renderingData != null && - renderingData.isMainSceneStage && - context.RecordSceneInjectionPoint( - m_injectionPoint); - } - } - public sealed class UniversalRenderer : ScriptableRenderer { private readonly UniversalRendererData m_rendererData; - private readonly UniversalSceneInjectionPass m_beforeOpaquePass = - new UniversalSceneInjectionPass( - RenderPassEvent.BeforeRenderingOpaques, - SceneRenderInjectionPoint.BeforeOpaque); private readonly DrawObjectsPass m_drawOpaqueObjectsPass = new DrawObjectsPass( RenderPassEvent.RenderOpaques, SceneRenderPhase.Opaque, RendererListDesc.CreateDefault( RendererListType.Opaque)); - private readonly UniversalSceneInjectionPass m_afterOpaquePass = - new UniversalSceneInjectionPass( - RenderPassEvent.AfterRenderingOpaques, - SceneRenderInjectionPoint.AfterOpaque); - private readonly UniversalSceneInjectionPass m_beforeSkyboxPass = - new UniversalSceneInjectionPass( - RenderPassEvent.BeforeRenderingSkybox, - SceneRenderInjectionPoint.BeforeSkybox); private readonly DrawSkyboxPass m_drawSkyboxPass = new DrawSkyboxPass(); - private readonly UniversalSceneInjectionPass m_afterSkyboxPass = - new UniversalSceneInjectionPass( - RenderPassEvent.AfterRenderingSkybox, - SceneRenderInjectionPoint.AfterSkybox); - private readonly UniversalSceneInjectionPass m_beforeTransparentPass = - new UniversalSceneInjectionPass( - RenderPassEvent.BeforeRenderingTransparents, - SceneRenderInjectionPoint.BeforeTransparent); private readonly DrawObjectsPass m_drawTransparentObjectsPass = new DrawObjectsPass( RenderPassEvent.RenderTransparents, SceneRenderPhase.Transparent, RendererListDesc.CreateDefault( RendererListType.Transparent)); - private readonly UniversalSceneInjectionPass m_afterTransparentPass = - new UniversalSceneInjectionPass( - RenderPassEvent.AfterRenderingTransparents, - SceneRenderInjectionPoint.AfterTransparent); public UniversalRenderer( UniversalRendererData rendererData) @@ -111,9 +63,7 @@ namespace XCEngine.Rendering.Universal SceneRenderPhase.Opaque, mainScene.opaqueRendererListDesc, mainScene.opaqueDrawingSettings); - EnqueuePass(m_beforeOpaquePass); EnqueuePass(m_drawOpaqueObjectsPass); - EnqueuePass(m_afterOpaquePass); } private void EnqueueSkyboxPasses( @@ -121,9 +71,7 @@ namespace XCEngine.Rendering.Universal { m_drawSkyboxPass.Configure( mainScene.skyboxPassEvent); - EnqueuePass(m_beforeSkyboxPass); EnqueuePass(m_drawSkyboxPass); - EnqueuePass(m_afterSkyboxPass); } private void EnqueueTransparentPasses( @@ -134,9 +82,7 @@ namespace XCEngine.Rendering.Universal SceneRenderPhase.Transparent, mainScene.transparentRendererListDesc, mainScene.transparentDrawingSettings); - EnqueuePass(m_beforeTransparentPass); EnqueuePass(m_drawTransparentObjectsPass); - EnqueuePass(m_afterTransparentPass); } } } diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRendererData.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRendererData.cs index d4773167..e8087cbe 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRendererData.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRendererData.cs @@ -13,7 +13,7 @@ namespace XCEngine.Rendering.Universal { mainScene = UniversalMainSceneData.CreateDefault(); rendererFeatures = - Array.Empty(); + CreateDefaultRendererFeatures(); } protected override ScriptableRenderer CreateRenderer() @@ -42,6 +42,16 @@ namespace XCEngine.Rendering.Universal return mainScene; } + + private static ScriptableRendererFeature[] + CreateDefaultRendererFeatures() + { + return new ScriptableRendererFeature[] + { + new BuiltinGaussianSplatRendererFeature(), + new BuiltinVolumetricRendererFeature() + }; + } } } diff --git a/managed/XCEngine.ScriptCore/InternalCalls.cs b/managed/XCEngine.ScriptCore/InternalCalls.cs index 8fc9468f..e9c87bf6 100644 --- a/managed/XCEngine.ScriptCore/InternalCalls.cs +++ b/managed/XCEngine.ScriptCore/InternalCalls.cs @@ -802,6 +802,12 @@ namespace XCEngine ulong nativeHandle, int injectionPoint); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool + Rendering_ScriptableRenderContext_RecordNativeSceneFeaturePass( + ulong nativeHandle, + string featurePassName); + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int Rendering_CameraRenderRequestContext_GetRenderedBaseCameraCount( diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs index d6c0ac51..de2dfc66 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderContext.cs @@ -69,6 +69,17 @@ namespace XCEngine.Rendering (int)injectionPoint); } + public bool RecordNativeSceneFeaturePass( + string featurePassName) + { + return !string.IsNullOrEmpty( + featurePassName) && + InternalCalls + .Rendering_ScriptableRenderContext_RecordNativeSceneFeaturePass( + m_nativeHandle, + featurePassName); + } + public bool DrawRenderers( SceneRenderPhase scenePhase, RendererListType rendererListType)