feat(srp): formalize renderer contracts and project feature bridge

This commit is contained in:
2026-04-20 15:03:45 +08:00
parent 10b092d467
commit a615f78e72
13 changed files with 1604 additions and 28 deletions

View File

@@ -1,4 +1,5 @@
using System;
using System.Reflection;
using XCEngine;
using XCEngine.Rendering;
using XCEngine.Rendering.Universal;
@@ -30,6 +31,34 @@ namespace Gameplay
}
}
internal static class ProbeRuntimeVersionUtility
{
private static readonly MethodInfo s_getRuntimeResourceVersionMethod =
typeof(ScriptableRenderPipelineAsset)
.GetMethod(
"GetRuntimeResourceVersionInstance",
BindingFlags.Instance |
BindingFlags.NonPublic);
public static int GetRuntimeResourceVersion(
ScriptableRenderPipelineAsset asset)
{
if (asset == null ||
s_getRuntimeResourceVersionMethod == null)
{
return 0;
}
object version =
s_getRuntimeResourceVersionMethod.Invoke(
asset,
null);
return version is int resolvedVersion
? resolvedVersion
: 0;
}
}
internal enum SceneInjectionKind
{
BeforeOpaque,
@@ -378,6 +407,140 @@ namespace Gameplay
}
}
internal static class ManagedFeaturePassOrderProbeState
{
public static string RecordedOrder = string.Empty;
public static void Reset()
{
RecordedOrder = string.Empty;
}
public static void Append(
string token)
{
if (string.IsNullOrEmpty(token))
{
return;
}
if (!string.IsNullOrEmpty(RecordedOrder))
{
RecordedOrder += ">";
}
RecordedOrder += token;
}
}
internal sealed class ManagedFeaturePassOrderProbePass
: ScriptableRenderPass
{
private readonly string m_token;
public ManagedFeaturePassOrderProbePass(
string token)
{
m_token = token ?? string.Empty;
renderPassEvent = RenderPassEvent.RenderOpaques;
}
protected override bool RecordRenderGraph(
ScriptableRenderContext context,
RenderingData renderingData)
{
if (renderingData == null ||
!renderingData.isMainSceneStage)
{
return false;
}
ManagedFeaturePassOrderProbeState.Append(m_token);
return true;
}
}
internal sealed class ManagedFeaturePassOrderBuiltinFeature
: ScriptableRendererFeature
{
private readonly ManagedFeaturePassOrderProbePass m_pass =
new ManagedFeaturePassOrderProbePass("Builtin");
public override void AddRenderPasses(
ScriptableRenderer renderer,
RenderingData renderingData)
{
if (renderer == null ||
renderingData == null ||
!renderingData.isMainSceneStage)
{
return;
}
renderer.EnqueuePass(m_pass);
}
}
internal sealed class ManagedFeaturePassOrderCustomFeature
: ScriptableRendererFeature
{
private readonly ManagedFeaturePassOrderProbePass m_pass;
public ManagedFeaturePassOrderCustomFeature(
string token)
{
m_pass =
new ManagedFeaturePassOrderProbePass(token);
}
public override void AddRenderPasses(
ScriptableRenderer renderer,
RenderingData renderingData)
{
if (renderer == null ||
renderingData == null ||
!renderingData.isMainSceneStage)
{
return;
}
renderer.EnqueuePass(m_pass);
}
}
internal sealed class ManagedFeaturePassOrderProbeRenderer
: ScriptableRenderer
{
public ManagedFeaturePassOrderProbeRenderer()
{
AddFeature(
new ManagedFeaturePassOrderBuiltinFeature());
}
}
internal sealed class ManagedFeaturePassOrderProbeRendererData
: ScriptableRendererData
{
protected override ScriptableRenderer CreateRenderer()
{
return new ManagedFeaturePassOrderProbeRenderer();
}
protected override ScriptableRendererFeature[] CreateRendererFeatures()
{
return new ScriptableRendererFeature[]
{
new ManagedFeaturePassOrderCustomFeature("CustomA"),
new ManagedFeaturePassOrderCustomFeature("CustomB")
};
}
protected override string GetPipelineRendererAssetKey()
{
return "BuiltinForward";
}
}
internal sealed class CameraDataObservationPass
: ScriptableRenderPass
{
@@ -892,6 +1055,74 @@ namespace Gameplay
}
}
internal sealed class ManagedRendererSelectionInactiveRenderer
: ScriptableRenderer
{
protected override bool SupportsRendererRecording(
RendererRecordingContext context)
{
return false;
}
protected override bool RecordRenderer(
RendererRecordingContext context)
{
return false;
}
}
internal sealed class ManagedRendererSelectionActiveRendererData
: ScriptableRendererData
{
protected override ScriptableRenderer CreateRenderer()
{
return new ProbeSceneRenderer();
}
protected override void ConfigureCameraRenderRequest(
CameraRenderRequestContext context)
{
if (HasDirectionalShadow(context))
{
ClearDirectionalShadow(context);
}
}
protected override void ConfigureCameraFramePlan(
ScriptableRenderPipelinePlanningContext context)
{
if (context == null ||
context.IsStageRequested(
CameraFrameStage.PostProcess))
{
return;
}
context.RequestFullscreenStage(
CameraFrameStage.PostProcess,
CameraFrameColorSource.MainSceneColor);
}
protected override string GetPipelineRendererAssetKey()
{
return "BuiltinForward";
}
}
internal sealed class ManagedRendererSelectionInactiveRendererData
: ScriptableRendererData
{
protected override ScriptableRenderer CreateRenderer()
{
return new ManagedRendererSelectionInactiveRenderer();
}
protected override string GetPipelineRendererAssetKey()
{
return "MissingBackend";
}
}
internal sealed class ManagedRendererReuseProbeRendererData
: ProbeRendererData
{
@@ -914,6 +1145,8 @@ namespace Gameplay
internal static class ManagedRendererInvalidationProbeState
{
public static int CreatePipelineCallCount;
public static int DisposePipelineCallCount;
public static int CreateRendererCallCount;
public static int SetupRendererCallCount;
public static int CreateFeatureCallCount;
@@ -923,6 +1156,8 @@ namespace Gameplay
public static void Reset()
{
CreatePipelineCallCount = 0;
DisposePipelineCallCount = 0;
CreateRendererCallCount = 0;
SetupRendererCallCount = 0;
CreateFeatureCallCount = 0;
@@ -932,6 +1167,30 @@ namespace Gameplay
}
}
internal sealed class ManagedRendererInvalidationProbePipeline
: RendererBackedRenderPipeline
{
public ManagedRendererInvalidationProbePipeline(
RendererBackedRenderPipelineAsset asset)
: base(asset)
{
ManagedRendererInvalidationProbeState
.CreatePipelineCallCount++;
}
protected override void Dispose(
bool disposing)
{
if (disposing)
{
ManagedRendererInvalidationProbeState
.DisposePipelineCallCount++;
}
base.Dispose(disposing);
}
}
internal sealed class ManagedRendererInvalidationProbeFeature
: ScriptableRendererFeature
{
@@ -982,6 +1241,79 @@ namespace Gameplay
}
}
internal static class ManagedPersistentFeatureProbeState
{
public static int CreateRendererCallCount;
public static int CreateFeatureRuntimeCallCount;
public static int DisposeRendererCallCount;
public static int DisposeFeatureCallCount;
public static int InvalidateRendererCallCount;
public static void Reset()
{
CreateRendererCallCount = 0;
CreateFeatureRuntimeCallCount = 0;
DisposeRendererCallCount = 0;
DisposeFeatureCallCount = 0;
InvalidateRendererCallCount = 0;
}
}
internal sealed class ManagedPersistentFeatureProbeRendererFeature
: ScriptableRendererFeature
{
public override void Create()
{
ManagedPersistentFeatureProbeState
.CreateFeatureRuntimeCallCount++;
}
protected override void ReleaseRuntimeResources()
{
ManagedPersistentFeatureProbeState
.DisposeFeatureCallCount++;
}
}
internal sealed class ManagedPersistentFeatureProbeRenderer
: ProbeSceneRenderer
{
protected override void ReleaseRuntimeResources()
{
ManagedPersistentFeatureProbeState
.DisposeRendererCallCount++;
}
}
internal sealed class ManagedPersistentFeatureProbeRendererData
: ProbeRendererData
{
private readonly ManagedPersistentFeatureProbeRendererFeature m_feature =
new ManagedPersistentFeatureProbeRendererFeature();
protected override ScriptableRenderer CreateProbeRenderer()
{
ManagedPersistentFeatureProbeState
.CreateRendererCallCount++;
return new ManagedPersistentFeatureProbeRenderer();
}
protected override ScriptableRendererFeature[] CreateRendererFeatures()
{
return new ScriptableRendererFeature[]
{
m_feature
};
}
public void InvalidateForTest()
{
ManagedPersistentFeatureProbeState
.InvalidateRendererCallCount++;
SetDirty();
}
}
internal sealed class ManagedPlannedFullscreenRenderPipelineProbeRendererData
: ProbeRendererData
{
@@ -1317,6 +1649,20 @@ namespace Gameplay
}
}
public sealed class ManagedFallbackRendererSelectionConsistencyProbeAsset
: RendererBackedRenderPipelineAsset
{
public ManagedFallbackRendererSelectionConsistencyProbeAsset()
{
rendererDataList = new ScriptableRendererData[]
{
new ManagedRendererSelectionActiveRendererData(),
new ManagedRendererSelectionInactiveRendererData()
};
defaultRendererIndex = 7;
}
}
public sealed class ManagedRendererReuseProbeAsset
: RendererBackedRenderPipelineAsset
{
@@ -1347,6 +1693,13 @@ namespace Gameplay
};
}
protected override ScriptableRenderPipeline
CreateRendererBackedPipeline()
{
return new ManagedRendererInvalidationProbePipeline(
this);
}
public void InvalidateDefaultRendererForTest()
{
if (m_rendererData == null)
@@ -1358,6 +1711,47 @@ namespace Gameplay
}
}
public sealed class ManagedPersistentFeatureProbeAsset
: RendererBackedRenderPipelineAsset
{
private readonly ManagedPersistentFeatureProbeRendererData
m_rendererData;
public ManagedPersistentFeatureProbeAsset()
{
ManagedPersistentFeatureProbeState.Reset();
m_rendererData =
new ManagedPersistentFeatureProbeRendererData();
rendererDataList = new ScriptableRendererData[]
{
m_rendererData
};
}
public void InvalidateDefaultRendererForTest()
{
if (m_rendererData == null)
{
return;
}
m_rendererData.InvalidateForTest();
}
}
public sealed class ManagedFeaturePassOrderProbeAsset
: RendererBackedRenderPipelineAsset
{
public ManagedFeaturePassOrderProbeAsset()
{
ManagedFeaturePassOrderProbeState.Reset();
rendererDataList = new ScriptableRendererData[]
{
new ManagedFeaturePassOrderProbeRendererData()
};
}
}
internal static class ManagedAssetInvalidationProbeState
{
public static int CreatePipelineCallCount;
@@ -1735,12 +2129,16 @@ namespace Gameplay
public sealed class ManagedRendererInvalidationObservationProbe
: MonoBehaviour
{
public int ObservedCreatePipelineCallCount;
public int ObservedDisposePipelineCallCount;
public int ObservedCreateRendererCallCount;
public int ObservedSetupRendererCallCount;
public int ObservedCreateFeatureCallCount;
public int ObservedDisposeRendererCallCount;
public int ObservedDisposeFeatureCallCount;
public int ObservedInvalidateRendererCallCount;
public int ObservedRuntimeResourceVersionBeforeInvalidation;
public int ObservedRuntimeResourceVersionAfterInvalidation;
private bool m_requestedInvalidation;
@@ -1754,10 +2152,24 @@ namespace Gameplay
ManagedRendererInvalidationProbeState
.CreateRendererCallCount > 0)
{
ObservedRuntimeResourceVersionBeforeInvalidation =
ProbeRuntimeVersionUtility
.GetRuntimeResourceVersion(
selectedAsset);
selectedAsset.InvalidateDefaultRendererForTest();
ObservedRuntimeResourceVersionAfterInvalidation =
ProbeRuntimeVersionUtility
.GetRuntimeResourceVersion(
selectedAsset);
m_requestedInvalidation = true;
}
ObservedCreatePipelineCallCount =
ManagedRendererInvalidationProbeState
.CreatePipelineCallCount;
ObservedDisposePipelineCallCount =
ManagedRendererInvalidationProbeState
.DisposePipelineCallCount;
ObservedCreateRendererCallCount =
ManagedRendererInvalidationProbeState.CreateRendererCallCount;
ObservedSetupRendererCallCount =
@@ -1773,6 +2185,82 @@ namespace Gameplay
}
}
public sealed class ManagedPersistentFeatureRuntimeSelectionProbe
: MonoBehaviour
{
public void Start()
{
GraphicsSettings.renderPipelineAsset =
new ManagedPersistentFeatureProbeAsset();
}
}
public sealed class ManagedPersistentFeatureObservationProbe
: MonoBehaviour
{
public int ObservedCreateRendererCallCount;
public int ObservedCreateFeatureRuntimeCallCount;
public int ObservedDisposeRendererCallCount;
public int ObservedDisposeFeatureCallCount;
public int ObservedInvalidateRendererCallCount;
private bool m_requestedInvalidation;
public void Update()
{
ManagedPersistentFeatureProbeAsset selectedAsset =
GraphicsSettings.renderPipelineAsset
as ManagedPersistentFeatureProbeAsset;
if (!m_requestedInvalidation &&
selectedAsset != null &&
ManagedPersistentFeatureProbeState
.CreateFeatureRuntimeCallCount > 0)
{
selectedAsset.InvalidateDefaultRendererForTest();
m_requestedInvalidation = true;
}
ObservedCreateRendererCallCount =
ManagedPersistentFeatureProbeState
.CreateRendererCallCount;
ObservedCreateFeatureRuntimeCallCount =
ManagedPersistentFeatureProbeState
.CreateFeatureRuntimeCallCount;
ObservedDisposeRendererCallCount =
ManagedPersistentFeatureProbeState
.DisposeRendererCallCount;
ObservedDisposeFeatureCallCount =
ManagedPersistentFeatureProbeState
.DisposeFeatureCallCount;
ObservedInvalidateRendererCallCount =
ManagedPersistentFeatureProbeState
.InvalidateRendererCallCount;
}
}
public sealed class ManagedFeaturePassOrderRuntimeSelectionProbe
: MonoBehaviour
{
public void Start()
{
GraphicsSettings.renderPipelineAsset =
new ManagedFeaturePassOrderProbeAsset();
}
}
public sealed class ManagedFeaturePassOrderObservationProbe
: MonoBehaviour
{
public string ObservedOrder = string.Empty;
public void Update()
{
ObservedOrder =
ManagedFeaturePassOrderProbeState.RecordedOrder ??
string.Empty;
}
}
public sealed class ManagedAssetInvalidationRuntimeSelectionProbe
: MonoBehaviour
{