diff --git a/docs/used/SRP_Universal录制契约收口计划_阶段归档_2026-04-19.md b/docs/used/SRP_Universal录制契约收口计划_阶段归档_2026-04-19.md new file mode 100644 index 00000000..6a03bd30 --- /dev/null +++ b/docs/used/SRP_Universal录制契约收口计划_阶段归档_2026-04-19.md @@ -0,0 +1,116 @@ +# SRP Universal 录制契约收口计划 2026-04-19 + +## 1. 阶段目标 + +本阶段不扩展新的渲染特性,重点收口当前 `Universal` 包层里仍然直接暴露给外部脚本的底层录制协议。 + +目标是把: + +1. `ScriptableRenderContextExtensions` +2. `CameraRenderRequestContextExtensions` + +这类偏 native recording contract 的 public extension surface, +收回到 `Universal` 包自己的 `ScriptableRenderPass` / `ScriptableRendererFeature` 基类中。 + +这样做完后,结构应当变成: + +`Core Context` +-> `Universal Pass/Feature Base` +-> `UniversalRenderer / 用户自定义 Universal Pass` + +而不是: + +`Core Context` +-> `public extension method` +-> `所有外部脚本都可直接访问 native recording contract` + +--- + +## 2. 当前问题 + +### 2.1 包层语义和底层协议还没有分开 + +当前 `XCEngine.RenderPipelines.Universal` 仍然公开暴露了: + +1. `RecordScene` +2. `RecordOpaqueScenePhase` +3. `RecordBeforeOpaqueInjection` +4. `RecordShaderVectorFullscreenPass` +5. `HasDirectionalShadow` +6. `ClearDirectionalShadow` + +这些接口本质上不是“稳定的首方包层 authoring API”, +而是当前 native host 的直接录制协议。 + +### 2.2 当前 public surface 会把 prototype 固化 + +如果继续让这些 public extension 留在包层: + +1. probe/tests 会继续把它们视为正式 API +2. 外部脚本会绕过 `Pass/Feature` 基类直接依赖 raw contract +3. 后续要把 `Universal` 真正做成 Unity 风格首方包时,会更难收口 + +### 2.3 当前更合理的 ownership + +这些能力更适合由: + +1. `ScriptableRenderPass` 以 protected helper 形式承接录制 +2. `ScriptableRendererFeature` 以 protected helper 形式承接 request policy + +也就是: + +1. 自定义 pass 通过 pass 基类调度 recording +2. 自定义 feature 通过 feature 基类配置 request +3. 外部脚本不再直接拿到 context extension helper + +--- + +## 3. 本阶段实施内容 + +### Step 1:把录制 helper 收回到 `ScriptableRenderPass` + +计划: + +1. 在 `ScriptableRenderPass` 增加 protected recording helper +2. 覆盖 scene / injection / fullscreen 这几类当前已存在能力 +3. 删除 `ScriptableRenderContextExtensions.cs` +4. 更新 `UniversalRenderer` 和 probe passes 的调用方式 + +### Step 2:把 request helper 收回到 `ScriptableRendererFeature` + +计划: + +1. 在 `ScriptableRendererFeature` 增加 protected request helper +2. 覆盖方向光阴影查询与清理 +3. 删除 `CameraRenderRequestContextExtensions.cs` +4. 更新 `DisableDirectionalShadowRendererFeature` 的调用方式 + +### Step 3:更新 API surface probe 与脚本测试 + +计划: + +1. 修改 `ScriptableRenderContextApiSurfaceProbe` +2. 不再把上述 extension type 视为应存在的 public surface +3. 更新相关 scripting tests,保证收口后的 API surface 被固定下来 + +--- + +## 4. 验收标准 + +完成后应满足: + +1. `XCEngine.RenderPipelines.Universal` 不再公开包含 `ScriptableRenderContextExtensions` +2. `XCEngine.RenderPipelines.Universal` 不再公开包含 `CameraRenderRequestContextExtensions` +3. `UniversalRenderer`、内建 feature、probe 自定义 pass 仍然能正常录制 +4. `scripting_tests`、`rendering_unit_tests`、`editor_tests` 通过 +5. `XCEditor` 编译通过并完成至少 10s 冒烟验证 + +--- + +## 5. 阶段后位置 + +这一刀做完后,下一小阶段再继续处理: + +1. `ScriptableRenderer.SupportsStageRenderGraph / RecordStageRenderGraph` 的公开边界 +2. `Universal` 内部 scene/fullscreen recording 的正式 package-local 组织 +3. 是否需要进一步把 renderer recording seam 变成更明确的首方包内契约 diff --git a/managed/CMakeLists.txt b/managed/CMakeLists.txt index 89eb851e..7bb1ec58 100644 --- a/managed/CMakeLists.txt +++ b/managed/CMakeLists.txt @@ -202,11 +202,9 @@ set(XCENGINE_RENDER_PIPELINES_UNIVERSAL_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Universal/RenderingDataResolver.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Universal/ShadowData.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Universal/ScriptableRenderPass.cs - ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Universal/ScriptableRenderContextExtensions.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Universal/ScriptableRenderer.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Universal/ScriptableRendererData.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Universal/ScriptableRendererFeature.cs - ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Universal/CameraRenderRequestContextExtensions.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Universal/StageColorData.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Universal/UniversalRenderPipeline.cs ${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Rendering/Universal/UniversalRenderer.cs diff --git a/managed/GameScripts/RenderPipelineApiProbe.cs b/managed/GameScripts/RenderPipelineApiProbe.cs index 8687ceee..b2d233bf 100644 --- a/managed/GameScripts/RenderPipelineApiProbe.cs +++ b/managed/GameScripts/RenderPipelineApiProbe.cs @@ -30,16 +30,39 @@ namespace Gameplay } } + internal enum SceneInjectionKind + { + BeforeOpaque, + AfterOpaque, + BeforeSkybox, + AfterSkybox, + BeforeTransparent, + AfterTransparent + } + + internal enum ScenePhaseKind + { + Opaque, + Skybox, + Transparent + } + + internal enum FullscreenPassKind + { + ColorScale, + ShaderVector + } + internal sealed class SceneInjectionPass : ScriptableRenderPass { - private readonly Func m_recordAction; + private readonly SceneInjectionKind m_injectionKind; public SceneInjectionPass( RenderPassEvent passEvent, - Func recordAction) + SceneInjectionKind injectionKind) { renderPassEvent = passEvent; - m_recordAction = recordAction; + m_injectionKind = injectionKind; } protected override bool RecordRenderGraph( @@ -49,23 +72,44 @@ namespace Gameplay return context != null && renderingData != null && renderingData.isMainSceneStage && - m_recordAction != null && - m_recordAction(context); + RecordInjection(context); + } + + private bool RecordInjection( + ScriptableRenderContext context) + { + switch (m_injectionKind) + { + case SceneInjectionKind.BeforeOpaque: + return RecordBeforeOpaqueInjection(context); + case SceneInjectionKind.AfterOpaque: + return RecordAfterOpaqueInjection(context); + case SceneInjectionKind.BeforeSkybox: + return RecordBeforeSkyboxInjection(context); + case SceneInjectionKind.AfterSkybox: + return RecordAfterSkyboxInjection(context); + case SceneInjectionKind.BeforeTransparent: + return RecordBeforeTransparentInjection(context); + case SceneInjectionKind.AfterTransparent: + return RecordAfterTransparentInjection(context); + default: + return false; + } } } internal sealed class ScenePhasePass : ScriptableRenderPass { - private readonly Func m_recordAction; + private readonly ScenePhaseKind m_phaseKind; private readonly Action m_onRecorded; public ScenePhasePass( RenderPassEvent passEvent, - Func recordAction, + ScenePhaseKind phaseKind, Action onRecorded = null) { renderPassEvent = passEvent; - m_recordAction = recordAction; + m_phaseKind = phaseKind; m_onRecorded = onRecorded; } @@ -76,8 +120,7 @@ namespace Gameplay bool recorded = context != null && renderingData != null && renderingData.isMainSceneStage && - m_recordAction != null && - m_recordAction(context); + RecordPhase(context); if (recorded && m_onRecorded != null) { m_onRecorded(); @@ -85,18 +128,53 @@ namespace Gameplay return recorded; } + + private bool RecordPhase( + ScriptableRenderContext context) + { + switch (m_phaseKind) + { + case ScenePhaseKind.Opaque: + return RecordOpaqueScenePhase(context); + case ScenePhaseKind.Skybox: + return RecordSkyboxScenePhase(context); + case ScenePhaseKind.Transparent: + return RecordTransparentScenePhase(context); + default: + return false; + } + } } internal sealed class FullscreenPass : ScriptableRenderPass { - private readonly Func m_recordAction; + private readonly FullscreenPassKind m_passKind; + private readonly Vector4 m_vectorPayload; + private readonly string m_shaderPath; + private readonly string m_passName; public FullscreenPass( RenderPassEvent passEvent, - Func recordAction) + Vector4 colorScale) { renderPassEvent = passEvent; - m_recordAction = recordAction; + m_passKind = FullscreenPassKind.ColorScale; + m_vectorPayload = colorScale; + m_shaderPath = string.Empty; + m_passName = string.Empty; + } + + public FullscreenPass( + RenderPassEvent passEvent, + string shaderPath, + Vector4 vectorPayload, + string passName = null) + { + renderPassEvent = passEvent; + m_passKind = FullscreenPassKind.ShaderVector; + m_vectorPayload = vectorPayload; + m_shaderPath = shaderPath ?? string.Empty; + m_passName = passName ?? string.Empty; } protected override bool RecordRenderGraph( @@ -105,8 +183,27 @@ namespace Gameplay { return context != null && renderingData != null && - m_recordAction != null && - m_recordAction(context); + RecordFullscreen(context); + } + + private bool RecordFullscreen( + ScriptableRenderContext context) + { + switch (m_passKind) + { + case FullscreenPassKind.ColorScale: + return RecordColorScaleFullscreenPass( + context, + m_vectorPayload); + case FullscreenPassKind.ShaderVector: + return RecordShaderVectorFullscreenPass( + context, + m_shaderPath, + m_vectorPayload, + m_passName); + default: + return false; + } } } @@ -128,40 +225,40 @@ namespace Gameplay m_beforeOpaquePass = new SceneInjectionPass( RenderPassEvent.BeforeRenderingOpaques, - context => context.RecordBeforeOpaqueInjection()); + SceneInjectionKind.BeforeOpaque); m_opaquePass = new ScenePhasePass( RenderPassEvent.RenderOpaques, - context => context.RecordOpaqueScenePhase(), + ScenePhaseKind.Opaque, onOpaqueRecorded); m_afterOpaquePass = new SceneInjectionPass( RenderPassEvent.AfterRenderingOpaques, - context => context.RecordAfterOpaqueInjection()); + SceneInjectionKind.AfterOpaque); m_beforeSkyboxPass = new SceneInjectionPass( RenderPassEvent.BeforeRenderingSkybox, - context => context.RecordBeforeSkyboxInjection()); + SceneInjectionKind.BeforeSkybox); m_skyboxPass = new ScenePhasePass( RenderPassEvent.RenderSkybox, - context => context.RecordSkyboxScenePhase()); + ScenePhaseKind.Skybox); m_afterSkyboxPass = new SceneInjectionPass( RenderPassEvent.AfterRenderingSkybox, - context => context.RecordAfterSkyboxInjection()); + SceneInjectionKind.AfterSkybox); m_beforeTransparentPass = new SceneInjectionPass( RenderPassEvent.BeforeRenderingTransparents, - context => context.RecordBeforeTransparentInjection()); + SceneInjectionKind.BeforeTransparent); m_transparentPass = new ScenePhasePass( RenderPassEvent.RenderTransparents, - context => context.RecordTransparentScenePhase()); + ScenePhaseKind.Transparent); m_afterTransparentPass = new SceneInjectionPass( RenderPassEvent.AfterRenderingTransparents, - context => context.RecordAfterTransparentInjection()); + SceneInjectionKind.AfterTransparent); } public override void AddRenderPasses( @@ -240,16 +337,14 @@ namespace Gameplay new FullscreenFeature( new FullscreenPass( RenderPassEvent.BeforeRenderingPostProcessing, - context => context.RecordShaderVectorFullscreenPass( - BuiltinShaderPaths.ColorScalePostProcess, - firstVectorPayload, - "ColorScale")), + BuiltinShaderPaths.ColorScalePostProcess, + firstVectorPayload, + "ColorScale"), new FullscreenPass( RenderPassEvent.AfterRenderingPostProcessing, - context => context.RecordShaderVectorFullscreenPass( - BuiltinShaderPaths.ColorScalePostProcess, - secondVectorPayload, - "ColorScale")))); + BuiltinShaderPaths.ColorScalePostProcess, + secondVectorPayload, + "ColorScale"))); } } @@ -264,8 +359,7 @@ namespace Gameplay new FullscreenFeature( new FullscreenPass( RenderPassEvent.BeforeRenderingPostProcessing, - context => context.RecordColorScaleFullscreenPass( - postProcessScale)))); + postProcessScale))); } } @@ -277,12 +371,10 @@ namespace Gameplay new FullscreenFeature( new FullscreenPass( RenderPassEvent.BeforeRenderingPostProcessing, - context => context.RecordColorScaleFullscreenPass( - new Vector4(1.05f, 1.0f, 0.95f, 1.0f))), + new Vector4(1.05f, 1.0f, 0.95f, 1.0f)), new FullscreenPass( RenderPassEvent.BeforeRenderingFinalOutput, - context => context.RecordColorScaleFullscreenPass( - new Vector4(1.05f, 1.0f, 0.95f, 1.0f))))); + new Vector4(1.05f, 1.0f, 0.95f, 1.0f)))); } } @@ -592,7 +684,7 @@ namespace Gameplay finalColorData.requiresProcessing; } RecordCallCount++; - return context.RecordScene(); + return RecordScene(context); } } @@ -812,7 +904,7 @@ namespace Gameplay } internal sealed class ManagedCameraRequestConfiguredRendererData - : ScriptableRendererData + : UniversalRendererData { protected override ScriptableRenderer CreateRenderer() { @@ -822,10 +914,9 @@ namespace Gameplay protected override void ConfigureCameraRenderRequest( CameraRenderRequestContext context) { - if (context != null && - context.HasDirectionalShadow()) + if (HasDirectionalShadow(context)) { - context.ClearDirectionalShadow(); + ClearDirectionalShadow(context); } } } @@ -840,25 +931,11 @@ namespace Gameplay } public sealed class ManagedCameraRequestConfiguredRenderPipelineProbeAsset - : ScriptableRenderPipelineAsset + : UniversalRenderPipelineAsset { - private readonly ManagedCameraRequestConfiguredRendererData - m_rendererData = - new ManagedCameraRequestConfiguredRendererData(); - - protected override ScriptableRenderPipeline CreatePipeline() + public ManagedCameraRequestConfiguredRenderPipelineProbeAsset() { - return new ManagedCameraRequestConfiguredRenderPipelineProbe(); - } - - protected override void ConfigureCameraRenderRequest( - CameraRenderRequestContext context) - { - if (context != null && - context.HasDirectionalShadow()) - { - context.ClearDirectionalShadow(); - } + rendererData = new ManagedCameraRequestConfiguredRendererData(); } } diff --git a/managed/XCEngine.ScriptCore/Rendering/Universal/CameraRenderRequestContextExtensions.cs b/managed/XCEngine.ScriptCore/Rendering/Universal/CameraRenderRequestContextExtensions.cs deleted file mode 100644 index a5942cb8..00000000 --- a/managed/XCEngine.ScriptCore/Rendering/Universal/CameraRenderRequestContextExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using XCEngine; -using XCEngine.Rendering; - -namespace XCEngine.Rendering.Universal -{ - public static class CameraRenderRequestContextExtensions - { - public static bool HasDirectionalShadow( - this CameraRenderRequestContext context) - { - return context != null && - InternalCalls - .Rendering_CameraRenderRequestContext_GetHasDirectionalShadow( - context.nativeHandle); - } - - public static void ClearDirectionalShadow( - this CameraRenderRequestContext context) - { - if (context == null) - { - return; - } - - InternalCalls - .Rendering_CameraRenderRequestContext_ClearDirectionalShadow( - context.nativeHandle); - } - } -} diff --git a/managed/XCEngine.ScriptCore/Rendering/Universal/ColorScalePostProcessRendererFeature.cs b/managed/XCEngine.ScriptCore/Rendering/Universal/ColorScalePostProcessRendererFeature.cs index eed38aa4..0e36e835 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Universal/ColorScalePostProcessRendererFeature.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Universal/ColorScalePostProcessRendererFeature.cs @@ -24,7 +24,8 @@ namespace XCEngine.Rendering.Universal renderingData != null && renderingData.isPostProcessStage && m_feature != null && - context.RecordColorScaleFullscreenPass( + RecordColorScaleFullscreenPass( + context, m_feature.colorScale); } } diff --git a/managed/XCEngine.ScriptCore/Rendering/Universal/DisableDirectionalShadowRendererFeature.cs b/managed/XCEngine.ScriptCore/Rendering/Universal/DisableDirectionalShadowRendererFeature.cs index f4e51efa..5e790d49 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Universal/DisableDirectionalShadowRendererFeature.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Universal/DisableDirectionalShadowRendererFeature.cs @@ -9,10 +9,9 @@ namespace XCEngine.Rendering.Universal public override void ConfigureCameraRenderRequest( CameraRenderRequestContext context) { - if (context != null && - context.HasDirectionalShadow()) + if (HasDirectionalShadow(context)) { - context.ClearDirectionalShadow(); + ClearDirectionalShadow(context); } } } diff --git a/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRenderContextExtensions.cs b/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRenderContextExtensions.cs deleted file mode 100644 index e201b396..00000000 --- a/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRenderContextExtensions.cs +++ /dev/null @@ -1,185 +0,0 @@ -using System; -using XCEngine; -using XCEngine.Rendering; - -namespace XCEngine.Rendering.Universal -{ - public static class ScriptableRenderContextExtensions - { - private enum RecordedScenePhase - { - Opaque = 0, - Skybox = 1, - Transparent = 3 - } - - private enum RecordedSceneInjectionPoint - { - BeforeOpaque = 0, - AfterOpaque = 1, - BeforeSkybox = 2, - AfterSkybox = 3, - BeforeTransparent = 4, - AfterTransparent = 5 - } - - private enum RecordedFullscreenPassType - { - ColorScale = 0, - ShaderVector = 1 - } - - public static bool RecordScene( - this ScriptableRenderContext context) - { - return context != null && - InternalCalls - .Rendering_ScriptableRenderContext_RecordScene( - context.nativeHandle); - } - - public static bool RecordOpaqueScenePhase( - this ScriptableRenderContext context) - { - return RecordScenePhaseInternal( - context, - RecordedScenePhase.Opaque); - } - - public static bool RecordSkyboxScenePhase( - this ScriptableRenderContext context) - { - return RecordScenePhaseInternal( - context, - RecordedScenePhase.Skybox); - } - - public static bool RecordTransparentScenePhase( - this ScriptableRenderContext context) - { - return RecordScenePhaseInternal( - context, - RecordedScenePhase.Transparent); - } - - public static bool RecordBeforeOpaqueInjection( - this ScriptableRenderContext context) - { - return RecordSceneInjectionPointInternal( - context, - RecordedSceneInjectionPoint.BeforeOpaque); - } - - public static bool RecordAfterOpaqueInjection( - this ScriptableRenderContext context) - { - return RecordSceneInjectionPointInternal( - context, - RecordedSceneInjectionPoint.AfterOpaque); - } - - public static bool RecordBeforeSkyboxInjection( - this ScriptableRenderContext context) - { - return RecordSceneInjectionPointInternal( - context, - RecordedSceneInjectionPoint.BeforeSkybox); - } - - public static bool RecordAfterSkyboxInjection( - this ScriptableRenderContext context) - { - return RecordSceneInjectionPointInternal( - context, - RecordedSceneInjectionPoint.AfterSkybox); - } - - public static bool RecordBeforeTransparentInjection( - this ScriptableRenderContext context) - { - return RecordSceneInjectionPointInternal( - context, - RecordedSceneInjectionPoint.BeforeTransparent); - } - - public static bool RecordAfterTransparentInjection( - this ScriptableRenderContext context) - { - return RecordSceneInjectionPointInternal( - context, - RecordedSceneInjectionPoint.AfterTransparent); - } - - public static bool RecordColorScaleFullscreenPass( - this ScriptableRenderContext context, - Vector4 colorScale) - { - return RecordFullscreenPassInternal( - context, - RecordedFullscreenPassType.ColorScale, - string.Empty, - string.Empty, - colorScale); - } - - public static bool RecordShaderVectorFullscreenPass( - this ScriptableRenderContext context, - string shaderPath, - Vector4 vectorPayload, - string passName = null) - { - if (string.IsNullOrEmpty(shaderPath)) - { - throw new ArgumentException( - "Fullscreen shader path cannot be null or empty.", - nameof(shaderPath)); - } - - return RecordFullscreenPassInternal( - context, - RecordedFullscreenPassType.ShaderVector, - shaderPath, - passName ?? string.Empty, - vectorPayload); - } - - private static bool RecordScenePhaseInternal( - ScriptableRenderContext context, - RecordedScenePhase scenePhase) - { - return context != null && - InternalCalls - .Rendering_ScriptableRenderContext_RecordScenePhase( - context.nativeHandle, - (int)scenePhase); - } - - private static bool RecordSceneInjectionPointInternal( - ScriptableRenderContext context, - RecordedSceneInjectionPoint injectionPoint) - { - return context != null && - InternalCalls - .Rendering_ScriptableRenderContext_RecordSceneInjectionPoint( - context.nativeHandle, - (int)injectionPoint); - } - - private static bool RecordFullscreenPassInternal( - ScriptableRenderContext context, - RecordedFullscreenPassType passType, - string shaderPath, - string passName, - Vector4 vectorPayload) - { - return context != null && - InternalCalls - .Rendering_ScriptableRenderContext_RecordFullscreenPass( - context.nativeHandle, - (int)passType, - shaderPath, - passName, - ref vectorPayload); - } - } -} diff --git a/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRenderPass.cs b/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRenderPass.cs index 6edcffdb..9c9c8fbb 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRenderPass.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRenderPass.cs @@ -1,3 +1,4 @@ +using System; using XCEngine; using XCEngine.Rendering; @@ -5,6 +6,29 @@ namespace XCEngine.Rendering.Universal { public abstract class ScriptableRenderPass { + private enum RecordedScenePhase + { + Opaque = 0, + Skybox = 1, + Transparent = 3 + } + + private enum RecordedSceneInjectionPoint + { + BeforeOpaque = 0, + AfterOpaque = 1, + BeforeSkybox = 2, + AfterSkybox = 3, + BeforeTransparent = 4, + AfterTransparent = 5 + } + + private enum RecordedFullscreenPassType + { + ColorScale = 0, + ShaderVector = 1 + } + protected ScriptableRenderPass() { } @@ -35,6 +59,120 @@ namespace XCEngine.Rendering.Universal ScriptableRenderContext context, RenderingData renderingData); + protected bool RecordScene( + ScriptableRenderContext context) + { + return context != null && + InternalCalls + .Rendering_ScriptableRenderContext_RecordScene( + context.nativeHandle); + } + + protected bool RecordOpaqueScenePhase( + ScriptableRenderContext context) + { + return RecordScenePhaseInternal( + context, + RecordedScenePhase.Opaque); + } + + protected bool RecordSkyboxScenePhase( + ScriptableRenderContext context) + { + return RecordScenePhaseInternal( + context, + RecordedScenePhase.Skybox); + } + + protected bool RecordTransparentScenePhase( + ScriptableRenderContext context) + { + return RecordScenePhaseInternal( + context, + RecordedScenePhase.Transparent); + } + + protected bool RecordBeforeOpaqueInjection( + ScriptableRenderContext context) + { + return RecordSceneInjectionPointInternal( + context, + RecordedSceneInjectionPoint.BeforeOpaque); + } + + protected bool RecordAfterOpaqueInjection( + ScriptableRenderContext context) + { + return RecordSceneInjectionPointInternal( + context, + RecordedSceneInjectionPoint.AfterOpaque); + } + + protected bool RecordBeforeSkyboxInjection( + ScriptableRenderContext context) + { + return RecordSceneInjectionPointInternal( + context, + RecordedSceneInjectionPoint.BeforeSkybox); + } + + protected bool RecordAfterSkyboxInjection( + ScriptableRenderContext context) + { + return RecordSceneInjectionPointInternal( + context, + RecordedSceneInjectionPoint.AfterSkybox); + } + + protected bool RecordBeforeTransparentInjection( + ScriptableRenderContext context) + { + return RecordSceneInjectionPointInternal( + context, + RecordedSceneInjectionPoint.BeforeTransparent); + } + + protected bool RecordAfterTransparentInjection( + ScriptableRenderContext context) + { + return RecordSceneInjectionPointInternal( + context, + RecordedSceneInjectionPoint.AfterTransparent); + } + + protected bool RecordColorScaleFullscreenPass( + ScriptableRenderContext context, + Vector4 colorScale) + { + return RecordFullscreenPassInternal( + context, + RecordedFullscreenPassType.ColorScale, + string.Empty, + string.Empty, + colorScale); + } + + protected bool RecordShaderVectorFullscreenPass( + ScriptableRenderContext context, + string shaderPath, + Vector4 vectorPayload, + string passName = null) + { + if (string.IsNullOrEmpty(shaderPath)) + { + throw new ArgumentException( + "Fullscreen shader path cannot be null or empty.", + nameof(shaderPath)); + } + + return RecordFullscreenPassInternal( + context, + RecordedFullscreenPassType.ShaderVector, + shaderPath, + passName ?? string.Empty, + vectorPayload); + } + internal static bool TryResolveStage( RenderPassEvent passEvent, out CameraFrameStage stage) @@ -65,6 +203,45 @@ namespace XCEngine.Rendering.Universal return false; } } + + private bool RecordScenePhaseInternal( + ScriptableRenderContext context, + RecordedScenePhase scenePhase) + { + return context != null && + InternalCalls + .Rendering_ScriptableRenderContext_RecordScenePhase( + context.nativeHandle, + (int)scenePhase); + } + + private bool RecordSceneInjectionPointInternal( + ScriptableRenderContext context, + RecordedSceneInjectionPoint injectionPoint) + { + return context != null && + InternalCalls + .Rendering_ScriptableRenderContext_RecordSceneInjectionPoint( + context.nativeHandle, + (int)injectionPoint); + } + + private bool RecordFullscreenPassInternal( + ScriptableRenderContext context, + RecordedFullscreenPassType passType, + string shaderPath, + string passName, + Vector4 vectorPayload) + { + return context != null && + InternalCalls + .Rendering_ScriptableRenderContext_RecordFullscreenPass( + context.nativeHandle, + (int)passType, + shaderPath, + passName, + ref vectorPayload); + } } } diff --git a/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRendererData.cs b/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRendererData.cs index 4e838011..0f2c7aa8 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRendererData.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRendererData.cs @@ -59,6 +59,28 @@ namespace XCEngine.Rendering.Universal return Array.Empty(); } + protected bool HasDirectionalShadow( + CameraRenderRequestContext context) + { + return context != null && + InternalCalls + .Rendering_CameraRenderRequestContext_GetHasDirectionalShadow( + context.nativeHandle); + } + + protected void ClearDirectionalShadow( + CameraRenderRequestContext context) + { + if (context == null) + { + return; + } + + InternalCalls + .Rendering_CameraRenderRequestContext_ClearDirectionalShadow( + context.nativeHandle); + } + private ScriptableRendererFeature[] GetRendererFeatures() { if (m_rendererFeatures == null) diff --git a/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRendererFeature.cs b/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRendererFeature.cs index e7aeaa0e..3f60ca35 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRendererFeature.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Universal/ScriptableRendererFeature.cs @@ -25,6 +25,28 @@ namespace XCEngine.Rendering.Universal RenderingData renderingData) { } + + protected bool HasDirectionalShadow( + CameraRenderRequestContext context) + { + return context != null && + InternalCalls + .Rendering_CameraRenderRequestContext_GetHasDirectionalShadow( + context.nativeHandle); + } + + protected void ClearDirectionalShadow( + CameraRenderRequestContext context) + { + if (context == null) + { + return; + } + + InternalCalls + .Rendering_CameraRenderRequestContext_ClearDirectionalShadow( + context.nativeHandle); + } } } diff --git a/managed/XCEngine.ScriptCore/Rendering/Universal/UniversalRenderer.cs b/managed/XCEngine.ScriptCore/Rendering/Universal/UniversalRenderer.cs index abfa0aa1..f5b98ec6 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Universal/UniversalRenderer.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Universal/UniversalRenderer.cs @@ -4,16 +4,29 @@ using XCEngine.Rendering; namespace XCEngine.Rendering.Universal { + internal enum UniversalSceneRecordingKind + { + BeforeOpaqueInjection, + OpaqueScene, + AfterOpaqueInjection, + BeforeSkyboxInjection, + SkyboxScene, + AfterSkyboxInjection, + BeforeTransparentInjection, + TransparentScene, + AfterTransparentInjection + } + internal sealed class UniversalSceneRecordingPass : ScriptableRenderPass { - private readonly Func m_recordAction; + private readonly UniversalSceneRecordingKind m_recordingKind; public UniversalSceneRecordingPass( RenderPassEvent passEvent, - Func recordAction) + UniversalSceneRecordingKind recordingKind) { renderPassEvent = passEvent; - m_recordAction = recordAction; + m_recordingKind = recordingKind; } protected override bool RecordRenderGraph( @@ -23,8 +36,35 @@ namespace XCEngine.Rendering.Universal return context != null && renderingData != null && renderingData.isMainSceneStage && - m_recordAction != null && - m_recordAction(context); + RecordSceneStep(context); + } + + private bool RecordSceneStep( + ScriptableRenderContext context) + { + switch (m_recordingKind) + { + case UniversalSceneRecordingKind.BeforeOpaqueInjection: + return RecordBeforeOpaqueInjection(context); + case UniversalSceneRecordingKind.OpaqueScene: + return RecordOpaqueScenePhase(context); + case UniversalSceneRecordingKind.AfterOpaqueInjection: + return RecordAfterOpaqueInjection(context); + case UniversalSceneRecordingKind.BeforeSkyboxInjection: + return RecordBeforeSkyboxInjection(context); + case UniversalSceneRecordingKind.SkyboxScene: + return RecordSkyboxScenePhase(context); + case UniversalSceneRecordingKind.AfterSkyboxInjection: + return RecordAfterSkyboxInjection(context); + case UniversalSceneRecordingKind.BeforeTransparentInjection: + return RecordBeforeTransparentInjection(context); + case UniversalSceneRecordingKind.TransparentScene: + return RecordTransparentScenePhase(context); + case UniversalSceneRecordingKind.AfterTransparentInjection: + return RecordAfterTransparentInjection(context); + default: + return false; + } } } @@ -34,39 +74,39 @@ namespace XCEngine.Rendering.Universal private readonly UniversalSceneRecordingPass m_beforeOpaquePass = new UniversalSceneRecordingPass( RenderPassEvent.BeforeRenderingOpaques, - context => context.RecordBeforeOpaqueInjection()); + UniversalSceneRecordingKind.BeforeOpaqueInjection); private readonly UniversalSceneRecordingPass m_opaquePass = new UniversalSceneRecordingPass( RenderPassEvent.RenderOpaques, - context => context.RecordOpaqueScenePhase()); + UniversalSceneRecordingKind.OpaqueScene); private readonly UniversalSceneRecordingPass m_afterOpaquePass = new UniversalSceneRecordingPass( RenderPassEvent.AfterRenderingOpaques, - context => context.RecordAfterOpaqueInjection()); + UniversalSceneRecordingKind.AfterOpaqueInjection); private readonly UniversalSceneRecordingPass m_beforeSkyboxPass = new UniversalSceneRecordingPass( RenderPassEvent.BeforeRenderingSkybox, - context => context.RecordBeforeSkyboxInjection()); + UniversalSceneRecordingKind.BeforeSkyboxInjection); private readonly UniversalSceneRecordingPass m_skyboxPass = new UniversalSceneRecordingPass( RenderPassEvent.RenderSkybox, - context => context.RecordSkyboxScenePhase()); + UniversalSceneRecordingKind.SkyboxScene); private readonly UniversalSceneRecordingPass m_afterSkyboxPass = new UniversalSceneRecordingPass( RenderPassEvent.AfterRenderingSkybox, - context => context.RecordAfterSkyboxInjection()); + UniversalSceneRecordingKind.AfterSkyboxInjection); private readonly UniversalSceneRecordingPass m_beforeTransparentPass = new UniversalSceneRecordingPass( RenderPassEvent.BeforeRenderingTransparents, - context => context.RecordBeforeTransparentInjection()); + UniversalSceneRecordingKind.BeforeTransparentInjection); private readonly UniversalSceneRecordingPass m_transparentPass = new UniversalSceneRecordingPass( RenderPassEvent.RenderTransparents, - context => context.RecordTransparentScenePhase()); + UniversalSceneRecordingKind.TransparentScene); private readonly UniversalSceneRecordingPass m_afterTransparentPass = new UniversalSceneRecordingPass( RenderPassEvent.AfterRenderingTransparents, - context => context.RecordAfterTransparentInjection()); + UniversalSceneRecordingKind.AfterTransparentInjection); public UniversalSceneFeature( UniversalRendererData rendererData)