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:
2026-04-20 02:48:16 +08:00
parent 5e88449e3d
commit d196ec9264
5 changed files with 466 additions and 24 deletions

View File

@@ -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) {