refactor(srp): add renderer data invalidation seam
Introduce ScriptableRendererData dirty/invalidation support so renderer and feature caches can be released and rebuilt within the same managed asset runtime. Add managed probes and scripting coverage for non-public dirty APIs and for renderer rebuild after invalidation, then archive the completed phase plan.
This commit is contained in:
139
docs/used/SRP_RendererData失效与重建接缝计划_2026-04-20.md
Normal file
139
docs/used/SRP_RendererData失效与重建接缝计划_2026-04-20.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# SRP RendererData 失效与重建接缝计划 2026-04-20
|
||||
|
||||
## 1. 阶段目标
|
||||
|
||||
上一阶段已经把 renderer feature setup 正式归位到:
|
||||
|
||||
`ScriptableRendererData -> ScriptableRenderer`
|
||||
|
||||
但现在还缺最后一条关键闭环:
|
||||
|
||||
当同一个 `ScriptableRendererData` 被修改后,旧的 renderer cache 该怎么失效,以及下一次使用时怎么重建。
|
||||
|
||||
当前系统只在更大的生命周期边界清缓存:
|
||||
|
||||
1. asset runtime 释放
|
||||
2. bridge generation 变化
|
||||
3. managed pipeline dispose
|
||||
|
||||
这意味着“同一个 asset 还活着,但 renderer data 已经变了”的情况,还没有正式 contract。
|
||||
|
||||
这一阶段的目标就是补上 Unity 风格的 renderer data invalidation seam。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前问题
|
||||
|
||||
### 2.1 renderer cache 只能在 runtime release 时整体释放
|
||||
|
||||
现在 `ScriptableRendererData` 已经缓存:
|
||||
|
||||
1. renderer instance
|
||||
2. renderer feature cache
|
||||
|
||||
但没有一个正式的“我变脏了,请把这套 renderer setup 丢掉”的入口。
|
||||
|
||||
### 2.2 同 asset、同 runtime 内部缺少 renderer rebuild 语义
|
||||
|
||||
对于后续编辑器和 SRP/URP 包来说,很常见的场景是:
|
||||
|
||||
1. 还在用同一个 pipeline asset
|
||||
2. 只是改了 renderer data
|
||||
3. 希望下一次录制时重建 renderer
|
||||
|
||||
这一层现在还只能靠 asset/runtime 整体重建绕过去。
|
||||
|
||||
### 2.3 测试还没锁住“同一个 runtime 内触发 renderer rebuild”
|
||||
|
||||
现有测试已经锁了:
|
||||
|
||||
1. renderer reuse
|
||||
2. 生命周期 release
|
||||
3. setup seam
|
||||
|
||||
但还没锁“同一个 asset runtime 内,dirty 之后旧 renderer dispose,新 renderer rebuild”。
|
||||
|
||||
---
|
||||
|
||||
## 3. 实施方案
|
||||
|
||||
### 3.1 给 `ScriptableRendererData` 增加正式 invalidation seam
|
||||
|
||||
新增非 public / protected 语义:
|
||||
|
||||
1. `SetDirty()`
|
||||
2. `isInvalidated`
|
||||
|
||||
`SetDirty()` 负责:
|
||||
|
||||
1. 释放当前 renderer cache
|
||||
2. 释放当前 feature cache
|
||||
3. 标记 data 已失效
|
||||
|
||||
### 3.2 renderer 下一次解析时自动重建
|
||||
|
||||
保持调用面不变:
|
||||
|
||||
`GetRendererInstance()`
|
||||
|
||||
但在 dirty 之后,下一次解析 renderer 时应:
|
||||
|
||||
1. 重新创建 renderer
|
||||
2. 重新 setup renderer
|
||||
3. 清掉 invalidated 状态
|
||||
|
||||
### 3.3 用 probe 锁住同 asset runtime 内的 rebuild 行为
|
||||
|
||||
新增一个专用 probe:
|
||||
|
||||
1. 第一次 `SupportsStageRenderGraph` 创建 renderer
|
||||
2. 运行时通过同一个 managed asset 调用 renderer data invalidation
|
||||
3. 第二次 `SupportsStageRenderGraph` 在同一个 asset runtime 里重建 renderer
|
||||
|
||||
---
|
||||
|
||||
## 4. 实施步骤
|
||||
|
||||
### Step 1:补 core invalidation seam
|
||||
|
||||
目标:
|
||||
|
||||
1. 修改 `ScriptableRendererData`
|
||||
2. 引入 renderer/feature cache 的单独释放辅助逻辑
|
||||
3. 增加 `SetDirty()` 与 `isInvalidated`
|
||||
|
||||
### Step 2:补 invalidation probe
|
||||
|
||||
目标:
|
||||
|
||||
1. 在 `RenderPipelineApiProbe.cs` 增加 renderer invalidation 专用 asset / renderer data / renderer / feature / 观察脚本
|
||||
2. 用同一个 managed asset handle 触发 invalidation
|
||||
|
||||
### Step 3:补 API probe 与脚本测试
|
||||
|
||||
目标:
|
||||
|
||||
1. 更新 `ScriptableRenderContextApiSurfaceProbe.cs`
|
||||
2. 更新 `test_mono_script_runtime.cpp`
|
||||
3. 锁住 dirty 之后的 create/setup/dispose 计数
|
||||
|
||||
### Step 4:验证与收口
|
||||
|
||||
目标:
|
||||
|
||||
1. 编译 `rendering_unit_tests`、`scripting_tests`、`XCEditor`
|
||||
2. 跑测试
|
||||
3. 跑旧版 `editor/bin/Debug/XCEngine.exe` 10s 冒烟并检查新的 `SceneReady`
|
||||
4. 归档 plan,提交并推送
|
||||
|
||||
---
|
||||
|
||||
## 5. 验收标准
|
||||
|
||||
完成后应满足:
|
||||
|
||||
1. `ScriptableRendererData` 存在正式 dirty / invalidated seam
|
||||
2. dirty 之后旧 renderer cache 和 feature cache 会被释放
|
||||
3. 同一个 asset runtime 内下一次解析 renderer 时会自动重建
|
||||
4. probe / scripting test 锁住重建行为
|
||||
5. old editor 编译、测试、冒烟全部通过
|
||||
@@ -898,6 +898,76 @@ namespace Gameplay
|
||||
}
|
||||
}
|
||||
|
||||
internal static class ManagedRendererInvalidationProbeState
|
||||
{
|
||||
public static int CreateRendererCallCount;
|
||||
public static int SetupRendererCallCount;
|
||||
public static int CreateFeatureCallCount;
|
||||
public static int DisposeRendererCallCount;
|
||||
public static int DisposeFeatureCallCount;
|
||||
public static int InvalidateRendererCallCount;
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
CreateRendererCallCount = 0;
|
||||
SetupRendererCallCount = 0;
|
||||
CreateFeatureCallCount = 0;
|
||||
DisposeRendererCallCount = 0;
|
||||
DisposeFeatureCallCount = 0;
|
||||
InvalidateRendererCallCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ManagedRendererInvalidationProbeFeature
|
||||
: ScriptableRendererFeature
|
||||
{
|
||||
protected override void ReleaseRuntimeResources()
|
||||
{
|
||||
ManagedRendererInvalidationProbeState.DisposeFeatureCallCount++;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ManagedRendererInvalidationProbeRenderer
|
||||
: ProbeSceneRenderer
|
||||
{
|
||||
protected override void ReleaseRuntimeResources()
|
||||
{
|
||||
ManagedRendererInvalidationProbeState.DisposeRendererCallCount++;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ManagedRendererInvalidationProbeRendererData
|
||||
: ProbeRendererData
|
||||
{
|
||||
protected override ScriptableRenderer CreateProbeRenderer()
|
||||
{
|
||||
ManagedRendererInvalidationProbeState.CreateRendererCallCount++;
|
||||
return new ManagedRendererInvalidationProbeRenderer();
|
||||
}
|
||||
|
||||
protected override void SetupRenderer(
|
||||
ScriptableRenderer renderer)
|
||||
{
|
||||
ManagedRendererInvalidationProbeState.SetupRendererCallCount++;
|
||||
base.SetupRenderer(renderer);
|
||||
}
|
||||
|
||||
protected override ScriptableRendererFeature[] CreateRendererFeatures()
|
||||
{
|
||||
ManagedRendererInvalidationProbeState.CreateFeatureCallCount++;
|
||||
return new ScriptableRendererFeature[]
|
||||
{
|
||||
new ManagedRendererInvalidationProbeFeature()
|
||||
};
|
||||
}
|
||||
|
||||
public void InvalidateForTest()
|
||||
{
|
||||
ManagedRendererInvalidationProbeState.InvalidateRendererCallCount++;
|
||||
SetDirty();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ManagedPlannedFullscreenRenderPipelineProbeRendererData
|
||||
: ProbeRendererData
|
||||
{
|
||||
@@ -1235,6 +1305,33 @@ namespace Gameplay
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ManagedRendererInvalidationProbeAsset
|
||||
: RendererBackedRenderPipelineAsset
|
||||
{
|
||||
private readonly ManagedRendererInvalidationProbeRendererData
|
||||
m_rendererData;
|
||||
|
||||
public ManagedRendererInvalidationProbeAsset()
|
||||
{
|
||||
ManagedRendererInvalidationProbeState.Reset();
|
||||
m_rendererData = new ManagedRendererInvalidationProbeRendererData();
|
||||
rendererDataList = new ScriptableRendererData[]
|
||||
{
|
||||
m_rendererData
|
||||
};
|
||||
}
|
||||
|
||||
public void InvalidateDefaultRendererForTest()
|
||||
{
|
||||
if (m_rendererData == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_rendererData.InvalidateForTest();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ManagedPlannedFullscreenRenderPipelineProbeAsset
|
||||
: UniversalRenderPipelineAsset
|
||||
{
|
||||
@@ -1517,6 +1614,57 @@ namespace Gameplay
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ManagedRendererInvalidationRuntimeSelectionProbe
|
||||
: MonoBehaviour
|
||||
{
|
||||
public void Start()
|
||||
{
|
||||
GraphicsSettings.renderPipelineAsset =
|
||||
new ManagedRendererInvalidationProbeAsset();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ManagedRendererInvalidationObservationProbe
|
||||
: MonoBehaviour
|
||||
{
|
||||
public int ObservedCreateRendererCallCount;
|
||||
public int ObservedSetupRendererCallCount;
|
||||
public int ObservedCreateFeatureCallCount;
|
||||
public int ObservedDisposeRendererCallCount;
|
||||
public int ObservedDisposeFeatureCallCount;
|
||||
public int ObservedInvalidateRendererCallCount;
|
||||
|
||||
private bool m_requestedInvalidation;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
ManagedRendererInvalidationProbeAsset selectedAsset =
|
||||
GraphicsSettings.renderPipelineAsset
|
||||
as ManagedRendererInvalidationProbeAsset;
|
||||
if (!m_requestedInvalidation &&
|
||||
selectedAsset != null &&
|
||||
ManagedRendererInvalidationProbeState
|
||||
.CreateRendererCallCount > 0)
|
||||
{
|
||||
selectedAsset.InvalidateDefaultRendererForTest();
|
||||
m_requestedInvalidation = true;
|
||||
}
|
||||
|
||||
ObservedCreateRendererCallCount =
|
||||
ManagedRendererInvalidationProbeState.CreateRendererCallCount;
|
||||
ObservedSetupRendererCallCount =
|
||||
ManagedRendererInvalidationProbeState.SetupRendererCallCount;
|
||||
ObservedCreateFeatureCallCount =
|
||||
ManagedRendererInvalidationProbeState.CreateFeatureCallCount;
|
||||
ObservedDisposeRendererCallCount =
|
||||
ManagedRendererInvalidationProbeState.DisposeRendererCallCount;
|
||||
ObservedDisposeFeatureCallCount =
|
||||
ManagedRendererInvalidationProbeState.DisposeFeatureCallCount;
|
||||
ObservedInvalidateRendererCallCount =
|
||||
ManagedRendererInvalidationProbeState.InvalidateRendererCallCount;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RenderPipelineApiProbe : MonoBehaviour
|
||||
{
|
||||
public bool InitialAssetWasNull;
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace Gameplay
|
||||
public bool HasRendererBackedRenderPipelineType;
|
||||
public bool HasRendererDrivenRenderPipelineType;
|
||||
public bool HasRendererDataSetupRenderer;
|
||||
public bool HasRendererDataSetDirty;
|
||||
public bool HasRendererDataIsInvalidated;
|
||||
public bool HasRendererSupportsRendererRecording;
|
||||
public bool HasRendererRecordRenderer;
|
||||
public bool HasPublicRendererSupportsStageRenderGraph;
|
||||
@@ -180,6 +182,17 @@ namespace Gameplay
|
||||
"SetupRenderer",
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.NonPublic) != null;
|
||||
HasRendererDataSetDirty =
|
||||
rendererDataType.GetMethod(
|
||||
"SetDirty",
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.NonPublic) != null;
|
||||
HasRendererDataIsInvalidated =
|
||||
rendererDataType.GetProperty(
|
||||
"isInvalidated",
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.Public) != null;
|
||||
HasRendererSupportsRendererRecording =
|
||||
rendererType.GetMethod(
|
||||
"SupportsRendererRecording",
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace XCEngine.Rendering.Universal
|
||||
{
|
||||
private ScriptableRendererFeature[] m_rendererFeatures;
|
||||
private ScriptableRenderer m_rendererInstance;
|
||||
private bool m_rendererInvalidated;
|
||||
|
||||
protected ScriptableRendererData()
|
||||
{
|
||||
@@ -26,6 +27,7 @@ namespace XCEngine.Rendering.Universal
|
||||
if (m_rendererInstance != null)
|
||||
{
|
||||
SetupRendererInstance(m_rendererInstance);
|
||||
m_rendererInvalidated = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +50,11 @@ namespace XCEngine.Rendering.Universal
|
||||
SetupRenderer(renderer);
|
||||
}
|
||||
|
||||
internal void SetDirtyInstance()
|
||||
{
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
internal string GetPipelineRendererAssetKeyInstance()
|
||||
{
|
||||
return GetPipelineRendererAssetKey();
|
||||
@@ -55,31 +62,9 @@ namespace XCEngine.Rendering.Universal
|
||||
|
||||
internal void ReleaseRuntimeResourcesInstance()
|
||||
{
|
||||
if (m_rendererInstance != null)
|
||||
{
|
||||
m_rendererInstance.ReleaseRuntimeResourcesInstance();
|
||||
m_rendererInstance = null;
|
||||
m_rendererFeatures = null;
|
||||
ReleaseRuntimeResources();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_rendererFeatures != null)
|
||||
{
|
||||
for (int i = 0; i < m_rendererFeatures.Length; ++i)
|
||||
{
|
||||
ScriptableRendererFeature rendererFeature =
|
||||
m_rendererFeatures[i];
|
||||
if (rendererFeature != null)
|
||||
{
|
||||
rendererFeature.ReleaseRuntimeResourcesInstance();
|
||||
}
|
||||
}
|
||||
|
||||
m_rendererFeatures = null;
|
||||
}
|
||||
|
||||
ReleaseRendererSetupCache();
|
||||
ReleaseRuntimeResources();
|
||||
m_rendererInvalidated = false;
|
||||
}
|
||||
|
||||
internal void ConfigureCameraRenderRequestInstance(
|
||||
@@ -163,6 +148,14 @@ namespace XCEngine.Rendering.Universal
|
||||
{
|
||||
}
|
||||
|
||||
protected bool isInvalidated
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_rendererInvalidated;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool HasDirectionalShadow(
|
||||
CameraRenderRequestContext context)
|
||||
{
|
||||
@@ -185,6 +178,12 @@ namespace XCEngine.Rendering.Universal
|
||||
context.nativeHandle);
|
||||
}
|
||||
|
||||
protected void SetDirty()
|
||||
{
|
||||
ReleaseRendererSetupCache();
|
||||
m_rendererInvalidated = true;
|
||||
}
|
||||
|
||||
protected void AddRendererFeature(
|
||||
ScriptableRenderer renderer,
|
||||
ScriptableRendererFeature rendererFeature)
|
||||
@@ -227,6 +226,34 @@ namespace XCEngine.Rendering.Universal
|
||||
|
||||
return m_rendererFeatures;
|
||||
}
|
||||
|
||||
private void ReleaseRendererSetupCache()
|
||||
{
|
||||
if (m_rendererInstance != null)
|
||||
{
|
||||
m_rendererInstance.ReleaseRuntimeResourcesInstance();
|
||||
m_rendererInstance = null;
|
||||
m_rendererFeatures = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_rendererFeatures == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_rendererFeatures.Length; ++i)
|
||||
{
|
||||
ScriptableRendererFeature rendererFeature =
|
||||
m_rendererFeatures[i];
|
||||
if (rendererFeature != null)
|
||||
{
|
||||
rendererFeature.ReleaseRuntimeResourcesInstance();
|
||||
}
|
||||
}
|
||||
|
||||
m_rendererFeatures = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1179,6 +1179,8 @@ TEST_F(
|
||||
bool hasRendererBackedRenderPipelineType = false;
|
||||
bool hasRendererDrivenRenderPipelineType = false;
|
||||
bool hasRendererDataSetupRenderer = false;
|
||||
bool hasRendererDataSetDirty = false;
|
||||
bool hasRendererDataIsInvalidated = false;
|
||||
bool hasRendererSupportsRendererRecording = false;
|
||||
bool hasRendererRecordRenderer = false;
|
||||
bool hasPublicRendererSupportsStageRenderGraph = false;
|
||||
@@ -1292,6 +1294,14 @@ TEST_F(
|
||||
selectionScript,
|
||||
"HasRendererDataSetupRenderer",
|
||||
hasRendererDataSetupRenderer));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(
|
||||
selectionScript,
|
||||
"HasRendererDataSetDirty",
|
||||
hasRendererDataSetDirty));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(
|
||||
selectionScript,
|
||||
"HasRendererDataIsInvalidated",
|
||||
hasRendererDataIsInvalidated));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(
|
||||
selectionScript,
|
||||
"HasRendererSupportsRendererRecording",
|
||||
@@ -1336,6 +1346,8 @@ TEST_F(
|
||||
EXPECT_TRUE(hasRendererBackedRenderPipelineType);
|
||||
EXPECT_TRUE(hasRendererDrivenRenderPipelineType);
|
||||
EXPECT_TRUE(hasRendererDataSetupRenderer);
|
||||
EXPECT_TRUE(hasRendererDataSetDirty);
|
||||
EXPECT_TRUE(hasRendererDataIsInvalidated);
|
||||
EXPECT_TRUE(hasRendererSupportsRendererRecording);
|
||||
EXPECT_TRUE(hasRendererRecordRenderer);
|
||||
EXPECT_FALSE(hasPublicRendererSupportsStageRenderGraph);
|
||||
@@ -3143,6 +3155,109 @@ TEST_F(
|
||||
secondRecorder->Shutdown();
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
MonoScriptRuntimeTest,
|
||||
ManagedRenderPipelineBridgeRebuildsRendererAfterRendererDataInvalidation) {
|
||||
Scene* runtimeScene =
|
||||
CreateScene("ManagedRendererInvalidationScene");
|
||||
GameObject* selectionObject =
|
||||
runtimeScene->CreateGameObject(
|
||||
"ManagedRendererInvalidationSelection");
|
||||
ScriptComponent* selectionScript =
|
||||
AddScript(
|
||||
selectionObject,
|
||||
"Gameplay",
|
||||
"ManagedRendererInvalidationRuntimeSelectionProbe");
|
||||
ASSERT_NE(selectionScript, nullptr);
|
||||
GameObject* observationObject =
|
||||
runtimeScene->CreateGameObject(
|
||||
"ManagedRendererInvalidationObservation");
|
||||
ScriptComponent* observationScript =
|
||||
AddScript(
|
||||
observationObject,
|
||||
"Gameplay",
|
||||
"ManagedRendererInvalidationObservationProbe");
|
||||
ASSERT_NE(observationScript, nullptr);
|
||||
|
||||
engine->OnRuntimeStart(runtimeScene);
|
||||
engine->OnUpdate(0.016f);
|
||||
|
||||
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor =
|
||||
XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor();
|
||||
ASSERT_TRUE(descriptor.IsValid());
|
||||
ASSERT_NE(descriptor.managedAssetHandle, 0u);
|
||||
EXPECT_EQ(descriptor.assemblyName, "GameScripts");
|
||||
EXPECT_EQ(descriptor.namespaceName, "Gameplay");
|
||||
EXPECT_EQ(descriptor.className, "ManagedRendererInvalidationProbeAsset");
|
||||
|
||||
const auto bridge =
|
||||
XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge();
|
||||
ASSERT_NE(bridge, nullptr);
|
||||
|
||||
std::shared_ptr<const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetRuntime>
|
||||
assetRuntime = bridge->CreateAssetRuntime(descriptor);
|
||||
ASSERT_NE(assetRuntime, nullptr);
|
||||
|
||||
std::unique_ptr<XCEngine::Rendering::RenderPipelineStageRecorder>
|
||||
recorder = assetRuntime->CreateStageRecorder();
|
||||
ASSERT_NE(recorder, nullptr);
|
||||
|
||||
const XCEngine::Rendering::RenderContext context = {};
|
||||
ASSERT_TRUE(recorder->Initialize(context));
|
||||
ASSERT_TRUE(
|
||||
recorder->SupportsStageRenderGraph(
|
||||
XCEngine::Rendering::CameraFrameStage::MainScene));
|
||||
|
||||
engine->OnUpdate(0.016f);
|
||||
|
||||
ASSERT_TRUE(
|
||||
recorder->SupportsStageRenderGraph(
|
||||
XCEngine::Rendering::CameraFrameStage::MainScene));
|
||||
|
||||
engine->OnUpdate(0.016f);
|
||||
EXPECT_TRUE(runtime->GetLastError().empty()) << runtime->GetLastError();
|
||||
|
||||
int observedCreateRendererCallCount = 0;
|
||||
int observedSetupRendererCallCount = 0;
|
||||
int observedCreateFeatureCallCount = 0;
|
||||
int observedDisposeRendererCallCount = 0;
|
||||
int observedDisposeFeatureCallCount = 0;
|
||||
int observedInvalidateRendererCallCount = 0;
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(
|
||||
observationScript,
|
||||
"ObservedCreateRendererCallCount",
|
||||
observedCreateRendererCallCount));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(
|
||||
observationScript,
|
||||
"ObservedSetupRendererCallCount",
|
||||
observedSetupRendererCallCount));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(
|
||||
observationScript,
|
||||
"ObservedCreateFeatureCallCount",
|
||||
observedCreateFeatureCallCount));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(
|
||||
observationScript,
|
||||
"ObservedDisposeRendererCallCount",
|
||||
observedDisposeRendererCallCount));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(
|
||||
observationScript,
|
||||
"ObservedDisposeFeatureCallCount",
|
||||
observedDisposeFeatureCallCount));
|
||||
EXPECT_TRUE(runtime->TryGetFieldValue(
|
||||
observationScript,
|
||||
"ObservedInvalidateRendererCallCount",
|
||||
observedInvalidateRendererCallCount));
|
||||
|
||||
EXPECT_EQ(observedCreateRendererCallCount, 2);
|
||||
EXPECT_EQ(observedSetupRendererCallCount, 2);
|
||||
EXPECT_EQ(observedCreateFeatureCallCount, 2);
|
||||
EXPECT_EQ(observedDisposeRendererCallCount, 1);
|
||||
EXPECT_EQ(observedDisposeFeatureCallCount, 1);
|
||||
EXPECT_EQ(observedInvalidateRendererCallCount, 1);
|
||||
|
||||
recorder->Shutdown();
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
MonoScriptRuntimeTest,
|
||||
ManagedStageRecorderRecordsMainSceneThroughScriptableRenderContext) {
|
||||
|
||||
Reference in New Issue
Block a user