diff --git a/docs/used/SRP_URP_CameraDepthPrepassPlanningPlan_完成归档_2026-04-21.md b/docs/used/SRP_URP_CameraDepthPrepassPlanningPlan_完成归档_2026-04-21.md new file mode 100644 index 00000000..007df6fa --- /dev/null +++ b/docs/used/SRP_URP_CameraDepthPrepassPlanningPlan_完成归档_2026-04-21.md @@ -0,0 +1,69 @@ +# SRP URP Camera Depth Prepass Planning Plan + +Date: 2026-04-21 + +## Goal + +Make the managed URP `DepthOnly` block fully usable instead of only structurally present. + +After the previous stage: + +- `UniversalRenderer` owns the default depth-prepass block +- managed passes can record into the `DepthOnly` stage + +But the managed side still cannot create the underlying `DepthOnlyRenderRequest` from the camera surface, so enabling the block alone does not produce an executable depth prepass. + +This stage closes that gap. + +## Current Problems + +1. `ScriptableRenderPipelinePlanningContext.RequestDepthOnlyStage()` only toggles stage existence based on an already-populated native request. +2. Managed URP has no planning API for "use the camera surface depth attachment as a default depth-prepass target". +3. `UniversalRenderer.depthPrepass` can be enabled in data, but that does not yet guarantee a valid `DepthOnly` request exists. + +## Scope + +### 1. Add a native planning helper for default camera depth prepass requests + +Required work: + +- derive a depth-only `RenderSurface` from the camera request surface +- preserve the camera surface dimensions, render area, sample description, depth attachment, and transition settings +- remove color attachments so the request becomes a true depth-only target +- fail cleanly if the camera surface cannot support a depth prepass + +### 2. Expose the helper through the managed planning context + +Required work: + +- add a managed planning API that requests a default `DepthOnly` stage from the camera surface +- keep the older boolean-only request API for compatibility + +### 3. Make `UniversalRenderer` use the new helper + +Required work: + +- when the renderer-owned depth-prepass block is enabled, request the default camera depth prepass through the new API +- clear the stage explicitly when the block is disabled + +## Out Of Scope + +- depth-normal prepass +- automatic depth-prepass requirement analysis +- depth texture consumer tracking +- deferred renderer + +## Done Criteria + +1. Managed planning can create a valid default `DepthOnlyRenderRequest` from the camera surface. +2. Enabling the renderer-owned depth-prepass block produces a real executable `DepthOnly` stage. +3. Old `XCEditor` builds in Debug. +4. Old editor smoke passes for at least about 10 seconds and a fresh `SceneReady` appears in `editor/bin/Debug/editor.log`. + +## Follow-Up + +Once this closes, the next SRP work can move to higher-value renderer composition again: + +- renderer variants / multiple renderer strategies +- richer prepass policies +- managed shadow block expansion diff --git a/engine/include/XCEngine/Rendering/Execution/CameraFramePlan.h b/engine/include/XCEngine/Rendering/Execution/CameraFramePlan.h index a7fcd9d2..d8eab454 100644 --- a/engine/include/XCEngine/Rendering/Execution/CameraFramePlan.h +++ b/engine/include/XCEngine/Rendering/Execution/CameraFramePlan.h @@ -30,6 +30,8 @@ struct CameraFrameColorChainPlan { struct CameraFramePlan { static RenderSurface BuildGraphManagedIntermediateSurfaceTemplate( const RenderSurface& surface); + static RenderSurface BuildCameraDepthOnlySurfaceTemplate( + const RenderSurface& surface); CameraRenderRequest request = {}; ShadowCasterRenderRequest shadowCaster = {}; diff --git a/engine/src/Rendering/Execution/CameraFramePlan.cpp b/engine/src/Rendering/Execution/CameraFramePlan.cpp index 85c3a6ad..bc6c5edf 100644 --- a/engine/src/Rendering/Execution/CameraFramePlan.cpp +++ b/engine/src/Rendering/Execution/CameraFramePlan.cpp @@ -49,6 +49,14 @@ RenderSurface CameraFramePlan::BuildGraphManagedIntermediateSurfaceTemplate( return graphManagedSurface; } +RenderSurface CameraFramePlan::BuildCameraDepthOnlySurfaceTemplate( + const RenderSurface& surface) { + RenderSurface depthOnlySurface = surface; + depthOnlySurface.SetColorAttachments({}); + depthOnlySurface.ClearClearColorOverride(); + return depthOnlySurface; +} + CameraFramePlan CameraFramePlan::FromRequest(const CameraRenderRequest& request) { CameraFramePlan plan = {}; plan.request = request; diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index 7ab21051..5d19ec84 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -5938,6 +5938,35 @@ InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_RequestDepthOnlyS : 0; } +mono_bool +InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_RequestCameraDepthOnlyStage( + uint64_t nativeHandle) { + ManagedScriptableRenderPipelinePlanningContextState* const state = + FindManagedScriptableRenderPipelinePlanningContextState(nativeHandle); + if (state == nullptr || state->plan == nullptr) { + return 0; + } + + Rendering::DepthOnlyRenderRequest& depthOnlyRequest = + state->plan->request.depthOnly; + depthOnlyRequest = {}; + depthOnlyRequest.surface = + Rendering::CameraFramePlan::BuildCameraDepthOnlySurfaceTemplate( + state->plan->request.surface); + depthOnlyRequest.clearFlags = + Rendering::RenderClearFlags::Depth; + + if (!depthOnlyRequest.IsValid()) { + depthOnlyRequest = {}; + state->plan->ClearDepthOnlyStage(true); + return 0; + } + + return state->plan->RequestDepthOnlyStage(true) + ? 1 + : 0; +} + void InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_ClearDepthOnlyStage( uint64_t nativeHandle) { ManagedScriptableRenderPipelinePlanningContextState* const state = @@ -6181,6 +6210,7 @@ void RegisterInternalCalls() { mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_ClearShadowCasterStage", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_ClearShadowCasterStage)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_GetHasExplicitShadowCasterStageConfiguration", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_GetHasExplicitShadowCasterStageConfiguration)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_RequestDepthOnlyStage", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_RequestDepthOnlyStage)); + mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_RequestCameraDepthOnlyStage", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_RequestCameraDepthOnlyStage)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_ClearDepthOnlyStage", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_ClearDepthOnlyStage)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_GetHasExplicitDepthOnlyStageConfiguration", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_GetHasExplicitDepthOnlyStageConfiguration)); mono_add_internal_call("XCEngine.InternalCalls::Rendering_ScriptableRenderPipelinePlanningContext_GetHasFinalColorProcessing", reinterpret_cast(&InternalCall_Rendering_ScriptableRenderPipelinePlanningContext_GetHasFinalColorProcessing)); diff --git a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs index 738e1436..65c5665f 100644 --- a/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs +++ b/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/UniversalRenderer.cs @@ -184,9 +184,9 @@ namespace XCEngine.Rendering.Universal DepthPrepassBlockData depthPrepass) { if (depthPrepass != null && - depthPrepass.enabled) + depthPrepass.enabled && + context.RequestCameraDepthOnlyStage()) { - context.RequestDepthOnlyStage(); return; } diff --git a/managed/XCEngine.ScriptCore/InternalCalls.cs b/managed/XCEngine.ScriptCore/InternalCalls.cs index 45594037..a41936b7 100644 --- a/managed/XCEngine.ScriptCore/InternalCalls.cs +++ b/managed/XCEngine.ScriptCore/InternalCalls.cs @@ -542,6 +542,11 @@ namespace XCEngine Rendering_ScriptableRenderPipelinePlanningContext_RequestDepthOnlyStage( ulong nativeHandle); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool + Rendering_ScriptableRenderPipelinePlanningContext_RequestCameraDepthOnlyStage( + ulong nativeHandle); + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void Rendering_ScriptableRenderPipelinePlanningContext_ClearDepthOnlyStage( diff --git a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelinePlanningContext.cs b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelinePlanningContext.cs index e39805f2..566c2733 100644 --- a/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelinePlanningContext.cs +++ b/managed/XCEngine.ScriptCore/Rendering/Core/ScriptableRenderPipelinePlanningContext.cs @@ -94,6 +94,13 @@ namespace XCEngine.Rendering m_nativeHandle); } + public bool RequestCameraDepthOnlyStage() + { + return InternalCalls + .Rendering_ScriptableRenderPipelinePlanningContext_RequestCameraDepthOnlyStage( + m_nativeHandle); + } + public void ClearDepthOnlyStage() { InternalCalls