#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #endif using namespace XCEngine::Components; using namespace XCEngine::Scripting; namespace { std::filesystem::path GetExecutableDirectory() { #ifdef _WIN32 std::wstring buffer(MAX_PATH, L'\0'); const DWORD length = GetModuleFileNameW(nullptr, buffer.data(), static_cast(buffer.size())); if (length == 0 || length >= buffer.size()) { return std::filesystem::current_path(); } buffer.resize(length); return std::filesystem::path(buffer).parent_path(); #else return std::filesystem::current_path(); #endif } std::filesystem::path ResolveProjectManagedOutputDirectory() { constexpr const char* configuredDirectory = XCENGINE_TEST_PROJECT_MANAGED_OUTPUT_DIR; if (configuredDirectory[0] != '\0') { return std::filesystem::path(configuredDirectory); } return (GetExecutableDirectory() / ".." / ".." / "managed" / "ProjectScriptAssemblies").lexically_normal(); } std::filesystem::path ResolveProjectScriptCoreDllPath() { constexpr const char* configuredPath = XCENGINE_TEST_PROJECT_SCRIPT_CORE_DLL; if (configuredPath[0] != '\0') { return std::filesystem::path(configuredPath); } return ResolveProjectManagedOutputDirectory() / "XCEngine.ScriptCore.dll"; } std::filesystem::path ResolveProjectGameScriptsDllPath() { constexpr const char* configuredPath = XCENGINE_TEST_PROJECT_GAME_SCRIPTS_DLL; if (configuredPath[0] != '\0') { return std::filesystem::path(configuredPath); } return ResolveProjectManagedOutputDirectory() / "GameScripts.dll"; } MonoScriptRuntime::Settings CreateProjectMonoSettings() { MonoScriptRuntime::Settings settings; settings.assemblyDirectory = ResolveProjectManagedOutputDirectory(); settings.corlibDirectory = settings.assemblyDirectory; settings.coreAssemblyPath = ResolveProjectScriptCoreDllPath(); settings.appAssemblyPath = ResolveProjectGameScriptsDllPath(); return settings; } class ProjectScriptAssemblyTest : public ::testing::Test { protected: void SetUp() override { engine = &ScriptEngine::Get(); engine->OnRuntimeStop(); engine->SetRuntimeFixedDeltaTime( ScriptEngine::DefaultFixedDeltaTime); XCEngine::Input::InputManager::Get().Shutdown(); XCEngine::Resources::ResourceManager::Get().Shutdown(); XCEngine::Rendering::Pipelines:: ClearConfiguredManagedRenderPipelineAssetDescriptor(); ASSERT_TRUE(std::filesystem::exists(ResolveProjectScriptCoreDllPath())); ASSERT_TRUE(std::filesystem::exists(ResolveProjectGameScriptsDllPath())); MonoScriptRuntime::Settings settings = CreateProjectMonoSettings(); std::string engineAssemblyError; ASSERT_TRUE( MonoScriptRuntime::DiscoverEngineAssemblies( settings, &engineAssemblyError)) << engineAssemblyError; ASSERT_FALSE(settings.engineAssemblies.empty()); for (const MonoScriptRuntime::ManagedAssemblyDescriptor& assembly : settings.engineAssemblies) { ASSERT_TRUE(std::filesystem::exists(assembly.path)) << assembly.path.string(); } runtime = std::make_unique(std::move(settings)); ASSERT_TRUE(runtime->Initialize()) << runtime->GetLastError(); engine->SetRuntime(runtime.get()); } void TearDown() override { if (engine != nullptr) { engine->OnRuntimeStop(); engine->SetRuntime(nullptr); } XCEngine::Input::InputManager::Get().Shutdown(); XCEngine::Rendering::Pipelines:: ClearConfiguredManagedRenderPipelineAssetDescriptor(); runtime.reset(); scene.reset(); XCEngine::Resources::ResourceManager::Get().Shutdown(); } Scene* CreateScene(const std::string& sceneName) { scene = std::make_unique(sceneName); return scene.get(); } ScriptComponent* AddProjectScript( GameObject* gameObject, const std::string& className) { ScriptComponent* component = gameObject->AddComponent(); component->SetScriptClass( "GameScripts", "ProjectScripts", className); return component; } ScriptEngine* engine = nullptr; std::unique_ptr runtime; std::unique_ptr scene; }; TEST_F(ProjectScriptAssemblyTest, InitializesFromProjectScriptAssemblyDirectory) { EXPECT_TRUE(runtime->IsInitialized()); EXPECT_EQ(runtime->GetSettings().assemblyDirectory, ResolveProjectManagedOutputDirectory()); EXPECT_EQ(runtime->GetSettings().appAssemblyPath, ResolveProjectGameScriptsDllPath()); } TEST_F(ProjectScriptAssemblyTest, DiscoversProjectAssetMonoBehaviourClassesAndFieldMetadata) { const std::vector classNames = runtime->GetScriptClassNames("GameScripts"); std::vector classDescriptors; ASSERT_TRUE(runtime->TryGetAvailableScriptClasses(classDescriptors)); EXPECT_TRUE(runtime->IsClassAvailable("GameScripts", "ProjectScripts", "ProjectScriptProbe")); EXPECT_NE( std::find(classNames.begin(), classNames.end(), "ProjectScripts.ProjectScriptProbe"), classNames.end()); EXPECT_NE( std::find( classDescriptors.begin(), classDescriptors.end(), ScriptClassDescriptor{"GameScripts", "ProjectScripts", "ProjectScriptProbe"}), classDescriptors.end()); std::vector fields; ASSERT_TRUE(runtime->TryGetClassFieldMetadata("GameScripts", "ProjectScripts", "ProjectScriptProbe", fields)); const std::vector expectedFields = { {"EnabledOnBoot", ScriptFieldType::Bool}, {"Label", ScriptFieldType::String}, {"Speed", ScriptFieldType::Float}, }; EXPECT_EQ(fields, expectedFields); std::vector defaultValues; ASSERT_TRUE(runtime->TryGetClassFieldDefaultValues("GameScripts", "ProjectScripts", "ProjectScriptProbe", defaultValues)); ASSERT_EQ(defaultValues.size(), 3u); EXPECT_EQ(defaultValues[0].fieldName, "EnabledOnBoot"); EXPECT_EQ(defaultValues[0].type, ScriptFieldType::Bool); EXPECT_EQ(std::get(defaultValues[0].value), true); EXPECT_EQ(defaultValues[1].fieldName, "Label"); EXPECT_EQ(defaultValues[1].type, ScriptFieldType::String); EXPECT_EQ(std::get(defaultValues[1].value), "ProjectScriptProbe"); EXPECT_EQ(defaultValues[2].fieldName, "Speed"); EXPECT_EQ(defaultValues[2].type, ScriptFieldType::Float); EXPECT_FLOAT_EQ(std::get(defaultValues[2].value), 2.5f); } TEST_F(ProjectScriptAssemblyTest, DiscoversProjectAssetRenderPipelineAssetClasses) { std::vector classes; ASSERT_TRUE(runtime->TryGetAvailableRenderPipelineAssetClasses(classes)); EXPECT_NE( std::find( classes.begin(), classes.end(), ScriptClassDescriptor{ "GameScripts", "ProjectScripts", "ProjectUniversalFeaturePipelineAsset"}), classes.end()); EXPECT_NE( std::find( classes.begin(), classes.end(), ScriptClassDescriptor{ "GameScripts", "ProjectScripts", "ProjectCustomRendererPipelineAsset"}), classes.end()); } TEST_F( ProjectScriptAssemblyTest, CreatesProjectAssetUniversalFeatureRuntimeThroughManagedBridge) { const auto bridge = XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge(); ASSERT_NE(bridge, nullptr); const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { "GameScripts", "ProjectScripts", "ProjectUniversalFeaturePipelineAsset" }; std::shared_ptr assetRuntime = bridge->CreateAssetRuntime(descriptor); ASSERT_NE(assetRuntime, nullptr); EXPECT_NE(assetRuntime->GetPipelineRendererAsset(), nullptr); std::unique_ptr recorder = assetRuntime->CreateStageRecorder(); ASSERT_NE(recorder, nullptr); const XCEngine::Rendering::RenderContext renderContext = {}; ASSERT_TRUE(recorder->Initialize(renderContext)); EXPECT_TRUE( recorder->SupportsStageRenderGraph( XCEngine::Rendering::CameraFrameStage::MainScene)); EXPECT_TRUE( recorder->SupportsStageRenderGraph( XCEngine::Rendering::CameraFrameStage::PostProcess)); XCEngine::Rendering::RenderGraph graph; XCEngine::Rendering::RenderGraphBuilder graphBuilder(graph); XCEngine::Rendering::RenderGraphTextureDesc colorDesc = {}; colorDesc.width = 64u; colorDesc.height = 64u; colorDesc.format = static_cast( XCEngine::RHI::Format::R8G8B8A8_UNorm); const XCEngine::Rendering::RenderGraphTextureHandle sourceColor = graphBuilder.ImportTexture( "ProjectManagedPostProcessSource", colorDesc, reinterpret_cast(901), {}); const XCEngine::Rendering::RenderGraphTextureHandle outputColor = graphBuilder.CreateTransientTexture( "ProjectManagedPostProcessOutput", colorDesc); const XCEngine::Rendering::RenderSceneData sceneData = {}; const XCEngine::Rendering::RenderSurface surface(64u, 64u); bool executionSucceeded = true; XCEngine::Rendering::RenderGraphBlackboard blackboard = {}; XCEngine::Rendering::EmplaceCameraFrameRenderGraphFrameData(blackboard) .resources.mainScene.color = sourceColor; const XCEngine::Rendering::RenderPipelineStageRenderGraphContext graphContext = { graphBuilder, "ProjectManagedPostProcess", XCEngine::Rendering::CameraFrameStage::PostProcess, renderContext, sceneData, surface, nullptr, nullptr, XCEngine::RHI::ResourceStates::Common, {}, { outputColor }, {}, {}, &executionSucceeded, &blackboard }; EXPECT_TRUE(recorder->RecordStageRenderGraph(graphContext)); XCEngine::Rendering::CompiledRenderGraph compiledGraph = {}; XCEngine::Containers::String errorMessage; ASSERT_TRUE( XCEngine::Rendering::RenderGraphCompiler::Compile( graph, compiledGraph, &errorMessage)) << errorMessage.CStr(); ASSERT_EQ(compiledGraph.GetPassCount(), 1u); EXPECT_STREQ( compiledGraph.GetPassName(0).CStr(), "ProjectManagedPostProcess"); recorder->Shutdown(); } TEST_F( ProjectScriptAssemblyTest, CreatesProjectCustomRendererPipelineAssetThroughManagedBridge) { const auto bridge = XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge(); ASSERT_NE(bridge, nullptr); const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { "GameScripts", "ProjectScripts", "ProjectCustomRendererPipelineAsset" }; std::shared_ptr assetRuntime = bridge->CreateAssetRuntime(descriptor); ASSERT_NE(assetRuntime, nullptr); EXPECT_NE(assetRuntime->GetPipelineRendererAsset(), nullptr); std::unique_ptr recorder = assetRuntime->CreateStageRecorder(); ASSERT_NE(recorder, nullptr); const XCEngine::Rendering::RenderContext renderContext = {}; ASSERT_TRUE(recorder->Initialize(renderContext)); EXPECT_TRUE( recorder->SupportsStageRenderGraph( XCEngine::Rendering::CameraFrameStage::MainScene)); EXPECT_FALSE( recorder->SupportsStageRenderGraph( XCEngine::Rendering::CameraFrameStage::PostProcess)); XCEngine::Rendering::RenderGraph graph; XCEngine::Rendering::RenderGraphBuilder graphBuilder(graph); XCEngine::Rendering::RenderGraphTextureDesc colorDesc = {}; colorDesc.width = 64u; colorDesc.height = 64u; colorDesc.format = static_cast( XCEngine::RHI::Format::R8G8B8A8_UNorm); XCEngine::Rendering::RenderGraphTextureDesc depthDesc = colorDesc; depthDesc.format = static_cast( XCEngine::RHI::Format::D32_Float); const XCEngine::Rendering::RenderGraphTextureHandle colorTarget = graphBuilder.CreateTransientTexture( "ProjectCustomMainSceneColor", colorDesc); const XCEngine::Rendering::RenderGraphTextureHandle depthTarget = graphBuilder.CreateTransientTexture( "ProjectCustomMainSceneDepth", depthDesc); const XCEngine::Rendering::RenderSceneData sceneData = {}; const XCEngine::Rendering::RenderSurface surface(64u, 64u); bool executionSucceeded = true; XCEngine::Rendering::RenderGraphBlackboard blackboard = {}; const XCEngine::Rendering::RenderPipelineStageRenderGraphContext graphContext = { graphBuilder, "ProjectCustomMainScene", XCEngine::Rendering::CameraFrameStage::MainScene, renderContext, sceneData, surface, nullptr, nullptr, XCEngine::RHI::ResourceStates::Common, {}, { colorTarget }, depthTarget, {}, &executionSucceeded, &blackboard }; EXPECT_TRUE(recorder->RecordStageRenderGraph(graphContext)); XCEngine::Rendering::CompiledRenderGraph compiledGraph = {}; XCEngine::Containers::String errorMessage; ASSERT_TRUE( XCEngine::Rendering::RenderGraphCompiler::Compile( graph, compiledGraph, &errorMessage)) << errorMessage.CStr(); ASSERT_EQ(compiledGraph.GetPassCount(), 1u); EXPECT_STREQ( compiledGraph.GetPassName(0).CStr(), "ProjectCustomMainScene.Opaque"); recorder->Shutdown(); } TEST_F( ProjectScriptAssemblyTest, ProjectManagedBridgeRebuildsRendererAfterProjectRendererDataInvalidation) { Scene* runtimeScene = CreateScene("ProjectRendererInvalidationScene"); GameObject* selectionObject = runtimeScene->CreateGameObject( "ProjectRendererInvalidationSelection"); ScriptComponent* selectionScript = AddProjectScript( selectionObject, "ProjectRendererInvalidationRuntimeSelectionProbe"); ASSERT_NE(selectionScript, nullptr); GameObject* observationObject = runtimeScene->CreateGameObject( "ProjectRendererInvalidationObservation"); ScriptComponent* observationScript = AddProjectScript( observationObject, "ProjectRendererInvalidationObservationProbe"); 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, "ProjectScripts"); EXPECT_EQ( descriptor.className, "ProjectRendererInvalidationProbeAsset"); const auto bridge = XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge(); ASSERT_NE(bridge, nullptr); std::shared_ptr assetRuntime = bridge->CreateAssetRuntime(descriptor); ASSERT_NE(assetRuntime, nullptr); std::unique_ptr 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 observedCreatePipelineCallCount = 0; int observedDisposePipelineCallCount = 0; int observedCreateRendererCallCount = 0; int observedSetupRendererCallCount = 0; int observedCreateFeatureCallCount = 0; int observedDisposeRendererCallCount = 0; int observedDisposeFeatureCallCount = 0; int observedInvalidateRendererCallCount = 0; int observedRuntimeResourceVersionBeforeInvalidation = 0; int observedRuntimeResourceVersionAfterInvalidation = 0; EXPECT_TRUE(runtime->TryGetFieldValue( observationScript, "ObservedCreatePipelineCallCount", observedCreatePipelineCallCount)); EXPECT_TRUE(runtime->TryGetFieldValue( observationScript, "ObservedDisposePipelineCallCount", observedDisposePipelineCallCount)); 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_TRUE(runtime->TryGetFieldValue( observationScript, "ObservedRuntimeResourceVersionBeforeInvalidation", observedRuntimeResourceVersionBeforeInvalidation)); EXPECT_TRUE(runtime->TryGetFieldValue( observationScript, "ObservedRuntimeResourceVersionAfterInvalidation", observedRuntimeResourceVersionAfterInvalidation)); EXPECT_EQ(observedCreatePipelineCallCount, 2); EXPECT_EQ(observedDisposePipelineCallCount, 1); EXPECT_EQ(observedCreateRendererCallCount, 2); EXPECT_EQ(observedSetupRendererCallCount, 2); EXPECT_EQ(observedCreateFeatureCallCount, 2); EXPECT_EQ(observedDisposeRendererCallCount, 1); EXPECT_EQ(observedDisposeFeatureCallCount, 1); EXPECT_EQ(observedInvalidateRendererCallCount, 1); EXPECT_GT(observedRuntimeResourceVersionBeforeInvalidation, 0); EXPECT_EQ( observedRuntimeResourceVersionAfterInvalidation, observedRuntimeResourceVersionBeforeInvalidation + 1); recorder->Shutdown(); } TEST_F( ProjectScriptAssemblyTest, ProjectManagedBridgeReleasesProjectRendererCachesAcrossInvalidationAndAssetRuntimeRelease) { Scene* runtimeScene = CreateScene("ProjectPersistentFeatureLifecycleScene"); GameObject* selectionObject = runtimeScene->CreateGameObject( "ProjectPersistentFeatureSelection"); ScriptComponent* selectionScript = AddProjectScript( selectionObject, "ProjectPersistentFeatureRuntimeSelectionProbe"); ASSERT_NE(selectionScript, nullptr); GameObject* observationObject = runtimeScene->CreateGameObject( "ProjectPersistentFeatureObservation"); ScriptComponent* observationScript = AddProjectScript( observationObject, "ProjectPersistentFeatureObservationProbe"); 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, "ProjectScripts"); EXPECT_EQ( descriptor.className, "ProjectPersistentFeatureProbeAsset"); { const auto bridge = XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge(); ASSERT_NE(bridge, nullptr); std::shared_ptr assetRuntime = bridge->CreateAssetRuntime(descriptor); ASSERT_NE(assetRuntime, nullptr); std::unique_ptr 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)); recorder->Shutdown(); } engine->OnUpdate(0.016f); EXPECT_TRUE(runtime->GetLastError().empty()) << runtime->GetLastError(); int observedCreateRendererCallCount = 0; int observedCreateFeatureRuntimeCallCount = 0; int observedDisposeRendererCallCount = 0; int observedDisposeFeatureCallCount = 0; int observedInvalidateRendererCallCount = 0; EXPECT_TRUE(runtime->TryGetFieldValue( observationScript, "ObservedCreateRendererCallCount", observedCreateRendererCallCount)); EXPECT_TRUE(runtime->TryGetFieldValue( observationScript, "ObservedCreateFeatureRuntimeCallCount", observedCreateFeatureRuntimeCallCount)); 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(observedCreateFeatureRuntimeCallCount, 2); EXPECT_EQ(observedDisposeRendererCallCount, 2); EXPECT_EQ(observedDisposeFeatureCallCount, 2); EXPECT_EQ(observedInvalidateRendererCallCount, 1); } TEST_F( ProjectScriptAssemblyTest, ProjectManagedBridgeRebuildsPipelineAfterProjectAssetInvalidation) { Scene* runtimeScene = CreateScene("ProjectAssetInvalidationScene"); GameObject* selectionObject = runtimeScene->CreateGameObject( "ProjectAssetInvalidationSelection"); ScriptComponent* selectionScript = AddProjectScript( selectionObject, "ProjectAssetInvalidationRuntimeSelectionProbe"); ASSERT_NE(selectionScript, nullptr); GameObject* observationObject = runtimeScene->CreateGameObject( "ProjectAssetInvalidationObservation"); ScriptComponent* observationScript = AddProjectScript( observationObject, "ProjectAssetInvalidationObservationProbe"); 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, "ProjectScripts"); EXPECT_EQ( descriptor.className, "ProjectAssetInvalidationProbeAsset"); const auto bridge = XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge(); ASSERT_NE(bridge, nullptr); std::shared_ptr assetRuntime = bridge->CreateAssetRuntime(descriptor); ASSERT_NE(assetRuntime, nullptr); std::unique_ptr 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)); ASSERT_FALSE( recorder->SupportsStageRenderGraph( XCEngine::Rendering::CameraFrameStage::PostProcess)); engine->OnUpdate(0.016f); ASSERT_FALSE( recorder->SupportsStageRenderGraph( XCEngine::Rendering::CameraFrameStage::MainScene)); ASSERT_TRUE( recorder->SupportsStageRenderGraph( XCEngine::Rendering::CameraFrameStage::PostProcess)); engine->OnUpdate(0.016f); EXPECT_TRUE(runtime->GetLastError().empty()) << runtime->GetLastError(); int observedCreatePipelineCallCount = 0; int observedDisposePipelineCallCount = 0; int observedInvalidateAssetCallCount = 0; int observedLastCreatedSupportedStage = 0; EXPECT_TRUE(runtime->TryGetFieldValue( observationScript, "ObservedCreatePipelineCallCount", observedCreatePipelineCallCount)); EXPECT_TRUE(runtime->TryGetFieldValue( observationScript, "ObservedDisposePipelineCallCount", observedDisposePipelineCallCount)); EXPECT_TRUE(runtime->TryGetFieldValue( observationScript, "ObservedInvalidateAssetCallCount", observedInvalidateAssetCallCount)); EXPECT_TRUE(runtime->TryGetFieldValue( observationScript, "ObservedLastCreatedSupportedStage", observedLastCreatedSupportedStage)); EXPECT_EQ(observedCreatePipelineCallCount, 2); EXPECT_EQ(observedDisposePipelineCallCount, 1); EXPECT_EQ(observedInvalidateAssetCallCount, 1); EXPECT_EQ( observedLastCreatedSupportedStage, static_cast( XCEngine::Rendering::CameraFrameStage::PostProcess)); recorder->Shutdown(); } } // namespace