diff --git a/editor/src/Commands/ProjectCommands.h b/editor/src/Commands/ProjectCommands.h index d2f5ee11..70ac6f07 100644 --- a/editor/src/Commands/ProjectCommands.h +++ b/editor/src/Commands/ProjectCommands.h @@ -492,7 +492,8 @@ inline bool SetProjectRenderPipelineAssetSelection( } auto& state = ::XCEngine::Rendering::GetGraphicsSettingsState(); - const auto previousDescriptor = state.GetRenderPipelineAssetDescriptor(); + const auto previousDescriptor = + state.GetConfiguredRenderPipelineAssetDescriptor(); const bool hasCurrentSelection = previousDescriptor.IsValid(); const bool selectingSameDescriptor = @@ -506,14 +507,14 @@ inline bool SetProjectRenderPipelineAssetSelection( } if (descriptor == nullptr) { - state.ClearRenderPipelineAssetDescriptor(); + state.ClearConfiguredRenderPipelineAssetDescriptor(); } else { ::XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor nativeDescriptor = {}; nativeDescriptor.assemblyName = descriptor->assemblyName; nativeDescriptor.namespaceName = descriptor->namespaceName; nativeDescriptor.className = descriptor->className; - state.SetRenderPipelineAssetDescriptor(nativeDescriptor); + state.SetConfiguredRenderPipelineAssetDescriptor(nativeDescriptor); } if (SaveProjectGraphicsSettings(context)) { @@ -521,9 +522,9 @@ inline bool SetProjectRenderPipelineAssetSelection( } if (previousDescriptor.IsValid()) { - state.SetRenderPipelineAssetDescriptor(previousDescriptor); + state.SetConfiguredRenderPipelineAssetDescriptor(previousDescriptor); } else { - state.ClearRenderPipelineAssetDescriptor(); + state.ClearConfiguredRenderPipelineAssetDescriptor(); } return false; } diff --git a/editor/src/UI/ProjectGraphicsSettingsDialog.h b/editor/src/UI/ProjectGraphicsSettingsDialog.h index a064ebd7..6417d6ae 100644 --- a/editor/src/UI/ProjectGraphicsSettingsDialog.h +++ b/editor/src/UI/ProjectGraphicsSettingsDialog.h @@ -82,7 +82,7 @@ inline void DrawProjectGraphicsSettingsDialog( pipelineAssetClasses); const auto currentDescriptor = ::XCEngine::Rendering::GetGraphicsSettingsState() - .GetRenderPipelineAssetDescriptor(); + .GetConfiguredRenderPipelineAssetDescriptor(); const std::string currentLabel = Detail::BuildRenderPipelineSelectionLabel( currentDescriptor, diff --git a/editor/src/Utils/ProjectGraphicsSettings.h b/editor/src/Utils/ProjectGraphicsSettings.h index 43f62bc0..6271aff6 100644 --- a/editor/src/Utils/ProjectGraphicsSettings.h +++ b/editor/src/Utils/ProjectGraphicsSettings.h @@ -11,7 +11,8 @@ namespace ProjectGraphicsSettings { inline ProjectFileUtils::GraphicsSettingsDescriptor BuildDescriptor( const ::XCEngine::Rendering::GraphicsSettingsState& state) { ProjectFileUtils::GraphicsSettingsDescriptor descriptor; - const auto nativeDescriptor = state.GetRenderPipelineAssetDescriptor(); + const auto nativeDescriptor = + state.GetConfiguredRenderPipelineAssetDescriptor(); if (nativeDescriptor.IsValid()) { descriptor.renderPipelineAssetAssembly = nativeDescriptor.assemblyName; descriptor.renderPipelineAssetNamespace = @@ -33,7 +34,7 @@ inline bool SaveSelection( inline void ApplySelection( const std::string& projectRoot, ::XCEngine::Rendering::GraphicsSettingsState& state) { - state.ClearRenderPipelineAssetDescriptor(); + state.ClearConfiguredRenderPipelineAssetDescriptor(); if (projectRoot.empty()) { return; @@ -53,7 +54,7 @@ inline void ApplySelection( savedDescriptor->renderPipelineAssetNamespace; nativeDescriptor.className = savedDescriptor->renderPipelineAssetClass; if (nativeDescriptor.IsValid()) { - state.SetRenderPipelineAssetDescriptor(nativeDescriptor); + state.SetConfiguredRenderPipelineAssetDescriptor(nativeDescriptor); } } diff --git a/engine/include/XCEngine/Rendering/GraphicsSettingsState.h b/engine/include/XCEngine/Rendering/GraphicsSettingsState.h index 6cda9dc6..963b3057 100644 --- a/engine/include/XCEngine/Rendering/GraphicsSettingsState.h +++ b/engine/include/XCEngine/Rendering/GraphicsSettingsState.h @@ -25,19 +25,19 @@ public: return m_environmentGeneration; } - void SetRenderPipelineAssetDescriptor( + void SetConfiguredRenderPipelineAssetDescriptor( const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor); - void ClearRenderPipelineAssetDescriptor(); + void ClearConfiguredRenderPipelineAssetDescriptor(); Pipelines::ManagedRenderPipelineAssetDescriptor - GetRenderPipelineAssetDescriptor() const { - return m_renderPipelineAssetDescriptor; + GetConfiguredRenderPipelineAssetDescriptor() const { + return m_configuredRenderPipelineAssetDescriptor; } private: void BumpEnvironmentGeneration(); Pipelines::ManagedRenderPipelineAssetDescriptor - m_renderPipelineAssetDescriptor = {}; + m_configuredRenderPipelineAssetDescriptor = {}; std::shared_ptr m_managedRenderPipelineBridge = nullptr; size_t m_managedRenderPipelineBridgeGeneration = 1u; diff --git a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h index 869ae521..f0812a54 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h @@ -61,7 +61,7 @@ private: ResolveManagedAssetRuntime() const; ManagedRenderPipelineAssetDescriptor m_descriptor; - ScriptableRenderPipelineHostAsset m_fallbackAsset; + ScriptableRenderPipelineHostAsset m_executionHostAsset; mutable std::shared_ptr m_managedAssetRuntime = nullptr; mutable size_t m_managedAssetRuntimeBridgeGeneration = 0u; @@ -108,12 +108,13 @@ std::shared_ptr GetManagedRenderPipelineBridge(); size_t GetManagedRenderPipelineEnvironmentGeneration(); -void SetManagedRenderPipelineAssetDescriptor( +void SetConfiguredManagedRenderPipelineAssetDescriptor( const ManagedRenderPipelineAssetDescriptor& descriptor); -void ClearManagedRenderPipelineAssetDescriptor(); -ManagedRenderPipelineAssetDescriptor GetManagedRenderPipelineAssetDescriptor(); +void ClearConfiguredManagedRenderPipelineAssetDescriptor(); +ManagedRenderPipelineAssetDescriptor +GetConfiguredManagedRenderPipelineAssetDescriptor(); std::shared_ptr -CreateManagedOrDefaultScriptableRenderPipelineAsset(); +CreateConfiguredManagedRenderPipelineAsset(); } // namespace Pipelines } // namespace Rendering diff --git a/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h b/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h index 3789d511..de940ea9 100644 --- a/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h +++ b/engine/include/XCEngine/Scripting/Mono/MonoScriptRuntime.h @@ -34,6 +34,11 @@ class MonoManagedRenderPipelineStageRecorder; class MonoScriptRuntime : public IScriptRuntime { public: + struct ManagedAssemblyDescriptor { + std::string name; + std::filesystem::path path; + }; + struct Settings { std::filesystem::path assemblyDirectory; std::filesystem::path corlibDirectory; @@ -41,6 +46,7 @@ public: std::filesystem::path appAssemblyPath; std::string coreAssemblyName = "XCEngine.ScriptCore"; std::string appAssemblyName = "GameScripts"; + std::vector engineAssemblies; std::string baseNamespace = "XCEngine"; std::string baseClassName = "MonoBehaviour"; }; @@ -179,6 +185,11 @@ private: uint32_t gcHandle = 0; }; + struct LoadedManagedAssemblyData { + MonoAssembly* assembly = nullptr; + MonoImage* image = nullptr; + }; + void ResolveSettings(); bool InitializeRootDomain(); bool CreateAppDomain(); @@ -186,6 +197,11 @@ private: void SetCurrentDomain() const; bool LoadAssemblies(); + bool LoadAssemblyImage( + const ManagedAssemblyDescriptor& descriptor, + const char* roleLabel, + MonoAssembly*& outAssembly, + MonoImage*& outImage); bool DiscoverScriptClasses(); void DiscoverScriptClassesInImage(const std::string& assemblyName, MonoImage* image); void DiscoverRenderPipelineAssetClassesInImage( @@ -223,6 +239,7 @@ private: void ClearManagedInstances(); void ClearExternalManagedObjects(); void ClearClassCache(); + MonoImage* FindLoadedAssemblyImage(const std::string& assemblyName) const; bool ResolveManagedClass( const std::string& assemblyName, const std::string& namespaceName, @@ -296,6 +313,8 @@ private: std::unordered_map m_classes; std::vector m_renderPipelineAssetClasses; + std::unordered_map + m_loadedManagedAssemblies; std::unordered_map m_instances; std::unordered_map m_externalManagedObjects; std::shared_ptr m_runtimeLifetimeToken; diff --git a/engine/src/Rendering/GraphicsSettingsState.cpp b/engine/src/Rendering/GraphicsSettingsState.cpp index 0efb13b4..2965c3a1 100644 --- a/engine/src/Rendering/GraphicsSettingsState.cpp +++ b/engine/src/Rendering/GraphicsSettingsState.cpp @@ -23,14 +23,14 @@ void GraphicsSettingsState::ClearManagedRenderPipelineBridge() { BumpEnvironmentGeneration(); } -void GraphicsSettingsState::SetRenderPipelineAssetDescriptor( +void GraphicsSettingsState::SetConfiguredRenderPipelineAssetDescriptor( const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor) { - m_renderPipelineAssetDescriptor = descriptor; + m_configuredRenderPipelineAssetDescriptor = descriptor; BumpEnvironmentGeneration(); } -void GraphicsSettingsState::ClearRenderPipelineAssetDescriptor() { - m_renderPipelineAssetDescriptor = {}; +void GraphicsSettingsState::ClearConfiguredRenderPipelineAssetDescriptor() { + m_configuredRenderPipelineAssetDescriptor = {}; BumpEnvironmentGeneration(); } diff --git a/engine/src/Rendering/Internal/RenderPipelineFactory.cpp b/engine/src/Rendering/Internal/RenderPipelineFactory.cpp index 560b490c..006667fa 100644 --- a/engine/src/Rendering/Internal/RenderPipelineFactory.cpp +++ b/engine/src/Rendering/Internal/RenderPipelineFactory.cpp @@ -8,15 +8,27 @@ namespace XCEngine { namespace Rendering { namespace Internal { -std::shared_ptr CreateDefaultRenderPipelineAsset() { - return Pipelines::CreateManagedOrDefaultScriptableRenderPipelineAsset(); +std::shared_ptr CreateConfiguredRenderPipelineAsset() { + return Pipelines::CreateConfiguredManagedRenderPipelineAsset(); +} + +std::shared_ptr CreateFallbackRenderPipelineAsset() { + return std::make_shared(); } std::shared_ptr ResolveRenderPipelineAssetOrDefault( std::shared_ptr preferredAsset) { - return preferredAsset != nullptr - ? std::move(preferredAsset) - : CreateDefaultRenderPipelineAsset(); + if (preferredAsset != nullptr) { + return std::move(preferredAsset); + } + + if (std::shared_ptr configuredAsset = + CreateConfiguredRenderPipelineAsset(); + configuredAsset != nullptr) { + return configuredAsset; + } + + return CreateFallbackRenderPipelineAsset(); } std::unique_ptr CreateRenderPipelineOrDefault( @@ -34,10 +46,24 @@ std::unique_ptr CreateRenderPipelineOrDefault( } } + const std::shared_ptr defaultAsset = + ResolveRenderPipelineAssetOrDefault(nullptr); + if (defaultAsset != nullptr && + defaultAsset != resolvedAsset) { + if (std::unique_ptr pipeline = + defaultAsset->CreatePipeline()) { + if (outResolvedAsset != nullptr) { + *outResolvedAsset = defaultAsset; + } + return pipeline; + } + } + const std::shared_ptr fallbackAsset = - CreateDefaultRenderPipelineAsset(); + CreateFallbackRenderPipelineAsset(); if (fallbackAsset != nullptr && - fallbackAsset != resolvedAsset) { + fallbackAsset != resolvedAsset && + fallbackAsset != defaultAsset) { if (std::unique_ptr pipeline = fallbackAsset->CreatePipeline()) { if (outResolvedAsset != nullptr) { @@ -51,7 +77,9 @@ std::unique_ptr CreateRenderPipelineOrDefault( *outResolvedAsset = fallbackAsset != nullptr ? fallbackAsset - : resolvedAsset; + : (defaultAsset != nullptr + ? defaultAsset + : resolvedAsset); } return std::make_unique(); } diff --git a/engine/src/Rendering/Internal/RenderPipelineFactory.h b/engine/src/Rendering/Internal/RenderPipelineFactory.h index ce3b937d..cd10066b 100644 --- a/engine/src/Rendering/Internal/RenderPipelineFactory.h +++ b/engine/src/Rendering/Internal/RenderPipelineFactory.h @@ -11,7 +11,8 @@ class RenderPipelineAsset; namespace Internal { -std::shared_ptr CreateDefaultRenderPipelineAsset(); +std::shared_ptr CreateConfiguredRenderPipelineAsset(); +std::shared_ptr CreateFallbackRenderPipelineAsset(); std::shared_ptr ResolveRenderPipelineAssetOrDefault( std::shared_ptr preferredAsset); diff --git a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp index 9c090735..b1b26d66 100644 --- a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp +++ b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp @@ -38,7 +38,8 @@ ManagedScriptableRenderPipelineAsset::ResolveManagedAssetRuntime() const { } std::unique_ptr ManagedScriptableRenderPipelineAsset::CreatePipeline() const { - std::unique_ptr pipeline = m_fallbackAsset.CreatePipeline(); + std::unique_ptr pipeline = + m_executionHostAsset.CreatePipeline(); auto* host = dynamic_cast(pipeline.get()); if (host == nullptr) { return pipeline; @@ -85,7 +86,7 @@ FinalColorSettings ManagedScriptableRenderPipelineAsset::GetDefaultFinalColorSet } } - return m_fallbackAsset.GetDefaultFinalColorSettings(); + return m_executionHostAsset.GetDefaultFinalColorSettings(); } void ManagedScriptableRenderPipelineAsset::ConfigureCameraFramePlan( @@ -118,29 +119,31 @@ size_t GetManagedRenderPipelineEnvironmentGeneration() { return GetGraphicsSettingsState().GetEnvironmentGeneration(); } -void SetManagedRenderPipelineAssetDescriptor( +void SetConfiguredManagedRenderPipelineAssetDescriptor( const ManagedRenderPipelineAssetDescriptor& descriptor) { - GetGraphicsSettingsState().SetRenderPipelineAssetDescriptor( + GetGraphicsSettingsState().SetConfiguredRenderPipelineAssetDescriptor( descriptor); } -void ClearManagedRenderPipelineAssetDescriptor() { - GetGraphicsSettingsState().ClearRenderPipelineAssetDescriptor(); +void ClearConfiguredManagedRenderPipelineAssetDescriptor() { + GetGraphicsSettingsState().ClearConfiguredRenderPipelineAssetDescriptor(); } -ManagedRenderPipelineAssetDescriptor GetManagedRenderPipelineAssetDescriptor() { - return GetGraphicsSettingsState().GetRenderPipelineAssetDescriptor(); +ManagedRenderPipelineAssetDescriptor +GetConfiguredManagedRenderPipelineAssetDescriptor() { + return GetGraphicsSettingsState() + .GetConfiguredRenderPipelineAssetDescriptor(); } std::shared_ptr -CreateManagedOrDefaultScriptableRenderPipelineAsset() { +CreateConfiguredManagedRenderPipelineAsset() { const ManagedRenderPipelineAssetDescriptor descriptor = - GetGraphicsSettingsState().GetRenderPipelineAssetDescriptor(); + GetGraphicsSettingsState().GetConfiguredRenderPipelineAssetDescriptor(); if (descriptor.IsValid()) { return std::make_shared(descriptor); } - return std::make_shared(); + return nullptr; } } // namespace Pipelines diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index ad6bbd3d..eded24d7 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -357,6 +357,14 @@ std::string TrimAssemblyName(const std::string& assemblyName) { return assemblyName; } +MonoScriptRuntime::ManagedAssemblyDescriptor BuildManagedAssemblyDescriptor( + const std::string& assemblyName, + const std::filesystem::path& assemblyPath) { + return MonoScriptRuntime::ManagedAssemblyDescriptor{ + assemblyName, + assemblyPath}; +} + bool IsMonoClassOrSubclass(MonoClass* monoClass, MonoClass* potentialBaseClass) { if (!monoClass || !potentialBaseClass) { return false; @@ -386,12 +394,14 @@ MonoScriptRuntime* GetActiveMonoScriptRuntime() { void ClearManagedRenderPipelineSelection(MonoScriptRuntime* runtime) { const Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = - Rendering::GetGraphicsSettingsState().GetRenderPipelineAssetDescriptor(); + Rendering::GetGraphicsSettingsState() + .GetConfiguredRenderPipelineAssetDescriptor(); if (runtime != nullptr && descriptor.managedAssetHandle != 0u) { runtime->ReleaseExternalManagedObject(descriptor.managedAssetHandle); } - Rendering::GetGraphicsSettingsState().ClearRenderPipelineAssetDescriptor(); + Rendering::GetGraphicsSettingsState() + .ClearConfiguredRenderPipelineAssetDescriptor(); } bool TryUnboxManagedBoolean(MonoObject* boxedValue, bool& outValue) { @@ -2812,7 +2822,8 @@ mono_bool InternalCall_Physics_Raycast( void InternalCall_Rendering_SetRenderPipelineAsset(MonoObject* assetObject) { MonoScriptRuntime* const runtime = GetActiveMonoScriptRuntime(); const Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor currentDescriptor = - Rendering::GetGraphicsSettingsState().GetRenderPipelineAssetDescriptor(); + Rendering::GetGraphicsSettingsState() + .GetConfiguredRenderPipelineAssetDescriptor(); if (assetObject == nullptr) { ClearManagedRenderPipelineSelection(runtime); @@ -2856,8 +2867,8 @@ void InternalCall_Rendering_SetRenderPipelineAsset(MonoObject* assetObject) { currentDescriptor.managedAssetHandle); } - Rendering::GetGraphicsSettingsState().SetRenderPipelineAssetDescriptor( - descriptor); + Rendering::GetGraphicsSettingsState() + .SetConfiguredRenderPipelineAssetDescriptor(descriptor); } MonoObject* InternalCall_Rendering_GetRenderPipelineAsset() { @@ -2867,7 +2878,8 @@ MonoObject* InternalCall_Rendering_GetRenderPipelineAsset() { } Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = - Rendering::GetGraphicsSettingsState().GetRenderPipelineAssetDescriptor(); + Rendering::GetGraphicsSettingsState() + .GetConfiguredRenderPipelineAssetDescriptor(); if (!descriptor.IsValid()) { return nullptr; } @@ -2877,8 +2889,8 @@ MonoObject* InternalCall_Rendering_GetRenderPipelineAsset() { return nullptr; } - Rendering::GetGraphicsSettingsState().SetRenderPipelineAssetDescriptor( - descriptor); + Rendering::GetGraphicsSettingsState() + .SetConfiguredRenderPipelineAssetDescriptor(descriptor); } return runtime->GetExternalManagedObject( @@ -3838,6 +3850,7 @@ void MonoScriptRuntime::Shutdown() { m_appAssembly = nullptr; m_coreImage = nullptr; m_appImage = nullptr; + m_loadedManagedAssemblies.clear(); m_componentClass = nullptr; m_behaviourClass = nullptr; m_gameObjectClass = nullptr; @@ -4304,6 +4317,19 @@ void MonoScriptRuntime::ResolveSettings() { m_settings.corlibDirectory = m_settings.coreAssemblyPath.parent_path(); } } + + for (ManagedAssemblyDescriptor& assembly : m_settings.engineAssemblies) { + if (assembly.name.empty() && !assembly.path.empty()) { + assembly.name = assembly.path.stem().string(); + } + + if (assembly.path.empty() && + !m_settings.assemblyDirectory.empty() && + !assembly.name.empty()) { + assembly.path = + m_settings.assemblyDirectory / (assembly.name + ".dll"); + } + } } bool MonoScriptRuntime::InitializeRootDomain() { @@ -4387,42 +4413,95 @@ bool MonoScriptRuntime::LoadAssemblies() { return false; } - if (!std::filesystem::exists(m_settings.coreAssemblyPath)) { - SetError("Script core assembly does not exist: " + m_settings.coreAssemblyPath.string()); - return false; - } - - if (!std::filesystem::exists(m_settings.appAssemblyPath)) { - SetError("Game scripts assembly does not exist: " + m_settings.appAssemblyPath.string()); - return false; - } - SetCurrentDomain(); + m_loadedManagedAssemblies.clear(); - m_coreAssembly = mono_domain_assembly_open(m_appDomain, m_settings.coreAssemblyPath.string().c_str()); - if (!m_coreAssembly) { - SetError("Failed to load script core assembly: " + m_settings.coreAssemblyPath.string()); + if (!LoadAssemblyImage( + BuildManagedAssemblyDescriptor( + m_settings.coreAssemblyName, + m_settings.coreAssemblyPath), + "script core", + m_coreAssembly, + m_coreImage)) { return false; } - m_coreImage = mono_assembly_get_image(m_coreAssembly); - if (!m_coreImage) { - SetError("Failed to access the script core image."); + for (const ManagedAssemblyDescriptor& assembly : + m_settings.engineAssemblies) { + MonoAssembly* loadedAssembly = nullptr; + MonoImage* loadedImage = nullptr; + if (!LoadAssemblyImage( + assembly, + "engine", + loadedAssembly, + loadedImage)) { + return false; + } + } + + if (!LoadAssemblyImage( + BuildManagedAssemblyDescriptor( + m_settings.appAssemblyName, + m_settings.appAssemblyPath), + "game scripts", + m_appAssembly, + m_appImage)) { return false; } - m_appAssembly = mono_domain_assembly_open(m_appDomain, m_settings.appAssemblyPath.string().c_str()); - if (!m_appAssembly) { - SetError("Failed to load game scripts assembly: " + m_settings.appAssemblyPath.string()); + return true; +} + +bool MonoScriptRuntime::LoadAssemblyImage( + const ManagedAssemblyDescriptor& descriptor, + const char* roleLabel, + MonoAssembly*& outAssembly, + MonoImage*& outImage) { + outAssembly = nullptr; + outImage = nullptr; + + if (descriptor.name.empty() || descriptor.path.empty()) { + SetError( + "Managed " + std::string(roleLabel) + + " assembly descriptor is incomplete."); return false; } - m_appImage = mono_assembly_get_image(m_appAssembly); - if (!m_appImage) { - SetError("Failed to access the game scripts image."); + if (!std::filesystem::exists(descriptor.path)) { + SetError( + "Managed " + std::string(roleLabel) + + " assembly does not exist: " + descriptor.path.string()); return false; } + if (m_loadedManagedAssemblies.contains(descriptor.name)) { + SetError( + "Managed assembly name is loaded more than once: " + + descriptor.name); + return false; + } + + outAssembly = mono_domain_assembly_open( + m_appDomain, + descriptor.path.string().c_str()); + if (!outAssembly) { + SetError( + "Failed to load managed " + std::string(roleLabel) + + " assembly: " + descriptor.path.string()); + return false; + } + + outImage = mono_assembly_get_image(outAssembly); + if (!outImage) { + SetError( + "Failed to access the managed " + std::string(roleLabel) + + " image: " + descriptor.path.string()); + return false; + } + + m_loadedManagedAssemblies.emplace( + descriptor.name, + LoadedManagedAssemblyData{outAssembly, outImage}); return true; } @@ -4568,6 +4647,15 @@ bool MonoScriptRuntime::DiscoverScriptClasses() { } DiscoverScriptClassesInImage(m_settings.appAssemblyName, m_appImage); + DiscoverRenderPipelineAssetClassesInImage( + m_settings.coreAssemblyName, + m_coreImage); + for (const ManagedAssemblyDescriptor& assembly : + m_settings.engineAssemblies) { + DiscoverRenderPipelineAssetClassesInImage( + assembly.name, + FindLoadedAssemblyImage(assembly.name)); + } DiscoverRenderPipelineAssetClassesInImage( m_settings.appAssemblyName, m_appImage); @@ -4669,11 +4757,16 @@ void MonoScriptRuntime::DiscoverRenderPipelineAssetClassesInImage( continue; } - m_renderPipelineAssetClasses.push_back( - ScriptClassDescriptor{ - assemblyName, - SafeString(mono_class_get_namespace(monoClass)), - SafeString(mono_class_get_name(monoClass))}); + const ScriptClassDescriptor descriptor{ + assemblyName, + SafeString(mono_class_get_namespace(monoClass)), + SafeString(mono_class_get_name(monoClass))}; + if (std::find( + m_renderPipelineAssetClasses.begin(), + m_renderPipelineAssetClasses.end(), + descriptor) == m_renderPipelineAssetClasses.end()) { + m_renderPipelineAssetClasses.push_back(descriptor); + } } } @@ -4899,13 +4992,7 @@ bool MonoScriptRuntime::ResolveManagedClass( return false; } - MonoImage* image = nullptr; - if (assemblyName == m_settings.coreAssemblyName) { - image = m_coreImage; - } else if (assemblyName == m_settings.appAssemblyName) { - image = m_appImage; - } - + MonoImage* image = FindLoadedAssemblyImage(assemblyName); if (!image) { return false; } @@ -5875,6 +5962,14 @@ void MonoScriptRuntime::ClearClassCache() { m_renderPipelineAssetClasses.clear(); } +MonoImage* MonoScriptRuntime::FindLoadedAssemblyImage( + const std::string& assemblyName) const { + const auto it = m_loadedManagedAssemblies.find(assemblyName); + return it != m_loadedManagedAssemblies.end() + ? it->second.image + : nullptr; +} + bool MonoScriptRuntime::InvokeManagedMethod( MonoObject* instance, MonoMethod* method, diff --git a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp index 09cb42e2..9c6157d4 100644 --- a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp +++ b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp @@ -707,6 +707,7 @@ TEST(BuiltinForwardPipeline_Test, RecordsMainSceneGraphPassWithSampledShadowDepe {}, { colorTarget }, depthTarget, + {}, &executionSucceeded, &blackboard }; @@ -818,6 +819,7 @@ TEST(NativeSceneRecorder_Test, RecordsExplicitSceneStepsInCallerOrder) { {}, { colorTarget }, depthTarget, + {}, &executionSucceeded, &blackboard }; @@ -922,6 +924,7 @@ TEST(NativeSceneRecorder_Test, RecordsSceneStepsThroughNativeSceneRendererContra {}, { colorTarget }, depthTarget, + {}, &executionSucceeded, &blackboard }; @@ -1467,6 +1470,7 @@ TEST(BuiltinForwardPipeline_Test, RecordsActiveFeatureInjectionPassesIntoMainSce {}, { colorTarget }, depthTarget, + {}, &executionSucceeded, &blackboard }; @@ -1567,6 +1571,7 @@ TEST(BuiltinForwardPipeline_Test, ForwardsSourceColorTextureIntoFeatureInjection sourceColor, { colorTarget }, depthTarget, + {}, &executionSucceeded, &blackboard }; @@ -2133,7 +2138,7 @@ TEST(BuiltinForwardPipeline_Test, OpenGLRuntimeTranspilesForwardShadowVariantToL std::string::npos); EXPECT_NE( runtimeSource.find( - "const float receiverDepth = shadowNdc.z * 0.5f + 0.5f - gShadowBiasAndTexelSize.x;"), + "const float receiverDepth = shadowNdc.z * 0.5f + 0.5f - gShadowSampling.receiverDepthBias;"), std::string::npos); XCEngine::RHI::CompiledSpirvShader spirvShader = {}; diff --git a/tests/Rendering/unit/test_camera_frame_graph_stage_contract.cpp b/tests/Rendering/unit/test_camera_frame_graph_stage_contract.cpp index d10e7b03..59da8cb5 100644 --- a/tests/Rendering/unit/test_camera_frame_graph_stage_contract.cpp +++ b/tests/Rendering/unit/test_camera_frame_graph_stage_contract.cpp @@ -1024,6 +1024,7 @@ TEST(RenderPipelineStageGraphContract_Test, RecordsFullscreenPassSequenceAndChai sourceColor, { outputColor }, {}, + {}, &executionSucceeded, &blackboard }; @@ -1101,6 +1102,7 @@ TEST(RenderPipelineStageGraphContract_Test, ResolvesManagedPostProcessSourceFrom {}, { outputColor }, {}, + {}, &executionSucceeded, &blackboard }; diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index ca86a730..ea2dc992 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -4343,6 +4343,7 @@ TEST(ScriptableRenderPipelineHost_Test, ForwardsRendererLifetimeAndFrameRenderin {}, {}, {}, + {}, &executionSucceeded, &blackboard }; @@ -4410,6 +4411,7 @@ TEST(ScriptableRenderPipelineHost_Test, PrefersStageRecorderBeforeFallbackRender {}, {}, {}, + {}, &executionSucceeded, &blackboard }; @@ -4473,6 +4475,7 @@ TEST( {}, {}, {}, + {}, &executionSucceeded, &blackboard }; @@ -4957,7 +4960,7 @@ TEST(CameraRenderer_Test, DefaultPipelineAssetUsesManagedSelectionWhenPresent) { "Gameplay", "ManagedRenderPipelineProbeAsset" }; - Pipelines::SetManagedRenderPipelineAssetDescriptor(descriptor); + Pipelines::SetConfiguredManagedRenderPipelineAssetDescriptor(descriptor); CameraRenderer renderer; auto* asset = @@ -4971,11 +4974,11 @@ TEST(CameraRenderer_Test, DefaultPipelineAssetUsesManagedSelectionWhenPresent) { dynamic_cast(renderer.GetPipeline()), nullptr); - Pipelines::ClearManagedRenderPipelineAssetDescriptor(); + Pipelines::ClearConfiguredManagedRenderPipelineAssetDescriptor(); } TEST(CameraRenderer_Test, DefaultPipelineAssetRefreshesWhenManagedSelectionChanges) { - Pipelines::ClearManagedRenderPipelineAssetDescriptor(); + Pipelines::ClearConfiguredManagedRenderPipelineAssetDescriptor(); CameraRenderer renderer; EXPECT_EQ( @@ -4988,7 +4991,7 @@ TEST(CameraRenderer_Test, DefaultPipelineAssetRefreshesWhenManagedSelectionChang "Gameplay", "ManagedRenderPipelineProbeAsset" }; - Pipelines::SetManagedRenderPipelineAssetDescriptor(descriptor); + Pipelines::SetConfiguredManagedRenderPipelineAssetDescriptor(descriptor); auto* managedAsset = dynamic_cast( @@ -4998,7 +5001,7 @@ TEST(CameraRenderer_Test, DefaultPipelineAssetRefreshesWhenManagedSelectionChang EXPECT_EQ(managedAsset->GetDescriptor().namespaceName, "Gameplay"); EXPECT_EQ(managedAsset->GetDescriptor().className, "ManagedRenderPipelineProbeAsset"); - Pipelines::ClearManagedRenderPipelineAssetDescriptor(); + Pipelines::ClearConfiguredManagedRenderPipelineAssetDescriptor(); EXPECT_EQ( dynamic_cast( renderer.GetPipelineAsset()), @@ -5011,7 +5014,7 @@ TEST(CameraRenderer_Test, RebindsToResolvedDefaultAssetWhenPreferredAssetCannotC "Gameplay", "ManagedRenderPipelineProbeAsset" }; - Pipelines::SetManagedRenderPipelineAssetDescriptor(descriptor); + Pipelines::SetConfiguredManagedRenderPipelineAssetDescriptor(descriptor); auto failingAssetState = std::make_shared(); failingAssetState->createNullPipeline = true; @@ -5031,7 +5034,7 @@ TEST(CameraRenderer_Test, RebindsToResolvedDefaultAssetWhenPreferredAssetCannotC EXPECT_EQ(asset->GetDescriptor().namespaceName, "Gameplay"); EXPECT_EQ(asset->GetDescriptor().className, "ManagedRenderPipelineProbeAsset"); - Pipelines::ClearManagedRenderPipelineAssetDescriptor(); + Pipelines::ClearConfiguredManagedRenderPipelineAssetDescriptor(); } TEST(CameraRenderer_Test, RecreatesManagedPipelineWhenBridgeChanges) { diff --git a/tests/Rendering/unit/test_graphics_settings_state.cpp b/tests/Rendering/unit/test_graphics_settings_state.cpp index be6c9a4b..48661614 100644 --- a/tests/Rendering/unit/test_graphics_settings_state.cpp +++ b/tests/Rendering/unit/test_graphics_settings_state.cpp @@ -17,7 +17,8 @@ class GraphicsSettingsStateScope final { public: GraphicsSettingsStateScope() : m_savedDescriptor( - GetGraphicsSettingsState().GetRenderPipelineAssetDescriptor()) + GetGraphicsSettingsState() + .GetConfiguredRenderPipelineAssetDescriptor()) , m_savedBridge( GetGraphicsSettingsState().GetManagedRenderPipelineBridge()) { } @@ -32,9 +33,9 @@ public: if (m_savedDescriptor.IsValid() || m_savedDescriptor.managedAssetHandle != 0u) { - state.SetRenderPipelineAssetDescriptor(m_savedDescriptor); + state.SetConfiguredRenderPipelineAssetDescriptor(m_savedDescriptor); } else { - state.ClearRenderPipelineAssetDescriptor(); + state.ClearConfiguredRenderPipelineAssetDescriptor(); } } @@ -49,7 +50,7 @@ private: TEST(GraphicsSettingsState_Test, StoresRenderPipelineAssetDescriptorAndBumpsEnvironmentGeneration) { GraphicsSettingsStateScope scope; GraphicsSettingsState& state = GetGraphicsSettingsState(); - state.ClearRenderPipelineAssetDescriptor(); + state.ClearConfiguredRenderPipelineAssetDescriptor(); const size_t generationBefore = state.GetEnvironmentGeneration(); const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { @@ -59,10 +60,10 @@ TEST(GraphicsSettingsState_Test, StoresRenderPipelineAssetDescriptorAndBumpsEnvi 42u }; - state.SetRenderPipelineAssetDescriptor(descriptor); + state.SetConfiguredRenderPipelineAssetDescriptor(descriptor); const Pipelines::ManagedRenderPipelineAssetDescriptor resolved = - state.GetRenderPipelineAssetDescriptor(); + state.GetConfiguredRenderPipelineAssetDescriptor(); EXPECT_EQ(resolved.assemblyName, descriptor.assemblyName); EXPECT_EQ(resolved.namespaceName, descriptor.namespaceName); EXPECT_EQ(resolved.className, descriptor.className); diff --git a/tests/editor/test_project_graphics_settings.cpp b/tests/editor/test_project_graphics_settings.cpp index 8ed5b7e6..47c6020e 100644 --- a/tests/editor/test_project_graphics_settings.cpp +++ b/tests/editor/test_project_graphics_settings.cpp @@ -37,7 +37,7 @@ TEST_F(ProjectGraphicsSettingsTest, BuildsDescriptorFromNativeSelection) { nativeDescriptor.namespaceName = "Gameplay"; nativeDescriptor.className = "ManagedForwardRenderPipelineProbeAsset"; nativeDescriptor.managedAssetHandle = 77u; - m_state.SetRenderPipelineAssetDescriptor(nativeDescriptor); + m_state.SetConfiguredRenderPipelineAssetDescriptor(nativeDescriptor); const auto descriptor = BuildDescriptor(m_state); EXPECT_EQ(descriptor.renderPipelineAssetAssembly, "GameScripts"); @@ -54,7 +54,7 @@ TEST_F(ProjectGraphicsSettingsTest, SavesNativeSelectionIntoProjectSettingsFile) nativeDescriptor.namespaceName = "Gameplay"; nativeDescriptor.className = "ManagedForwardRenderPipelineProbeAsset"; nativeDescriptor.managedAssetHandle = 88u; - m_state.SetRenderPipelineAssetDescriptor(nativeDescriptor); + m_state.SetConfiguredRenderPipelineAssetDescriptor(nativeDescriptor); ASSERT_TRUE(SaveSelection(m_projectRoot.string(), m_state)); @@ -82,7 +82,8 @@ TEST_F( ApplySelection(m_projectRoot.string(), m_state); - const auto nativeDescriptor = m_state.GetRenderPipelineAssetDescriptor(); + const auto nativeDescriptor = + m_state.GetConfiguredRenderPipelineAssetDescriptor(); EXPECT_EQ(nativeDescriptor.assemblyName, "GameScripts"); EXPECT_EQ(nativeDescriptor.namespaceName, "Gameplay"); EXPECT_EQ( @@ -99,13 +100,13 @@ TEST_F( nativeDescriptor.assemblyName = "GameScripts"; nativeDescriptor.className = "ManagedForwardRenderPipelineProbeAsset"; nativeDescriptor.managedAssetHandle = 99u; - m_state.SetRenderPipelineAssetDescriptor(nativeDescriptor); + m_state.SetConfiguredRenderPipelineAssetDescriptor(nativeDescriptor); ApplySelection(m_projectRoot.string(), m_state); - EXPECT_FALSE(m_state.GetRenderPipelineAssetDescriptor().IsValid()); + EXPECT_FALSE(m_state.GetConfiguredRenderPipelineAssetDescriptor().IsValid()); EXPECT_EQ( - m_state.GetRenderPipelineAssetDescriptor().managedAssetHandle, + m_state.GetConfiguredRenderPipelineAssetDescriptor().managedAssetHandle, 0u); } diff --git a/tests/scripting/test_mono_script_runtime.cpp b/tests/scripting/test_mono_script_runtime.cpp index c7475953..2940919d 100644 --- a/tests/scripting/test_mono_script_runtime.cpp +++ b/tests/scripting/test_mono_script_runtime.cpp @@ -133,7 +133,7 @@ protected: engine->SetRuntimeFixedDeltaTime(ScriptEngine::DefaultFixedDeltaTime); XCEngine::Input::InputManager::Get().Shutdown(); XCEngine::Resources::ResourceManager::Get().Shutdown(); - XCEngine::Rendering::Pipelines::ClearManagedRenderPipelineAssetDescriptor(); + XCEngine::Rendering::Pipelines::ClearConfiguredManagedRenderPipelineAssetDescriptor(); runtime = std::make_unique(CreateMonoSettings()); ASSERT_TRUE(runtime->Initialize()) << runtime->GetLastError(); @@ -145,7 +145,7 @@ protected: engine->OnRuntimeStop(); engine->SetRuntime(nullptr); XCEngine::Input::InputManager::Get().Shutdown(); - XCEngine::Rendering::Pipelines::ClearManagedRenderPipelineAssetDescriptor(); + XCEngine::Rendering::Pipelines::ClearConfiguredManagedRenderPipelineAssetDescriptor(); runtime.reset(); scene.reset(); XCEngine::Resources::ResourceManager::Get().Shutdown(); @@ -235,6 +235,15 @@ TEST_F( return lhs.className < rhs.className; })); + EXPECT_NE( + std::find( + classes.begin(), + classes.end(), + ScriptClassDescriptor{ + "XCEngine.ScriptCore", + "XCEngine.Rendering.FirstParty", + "ForwardRenderPipelineAsset"}), + classes.end()); EXPECT_NE( std::find( classes.begin(), @@ -376,7 +385,7 @@ TEST_F(MonoScriptRuntimeTest, ManagedGraphicsSettingsRoundTripsRenderPipelineAss engine->OnUpdate(0.016f); const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = - XCEngine::Rendering::Pipelines::GetManagedRenderPipelineAssetDescriptor(); + XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor(); EXPECT_EQ(descriptor.assemblyName, "GameScripts"); EXPECT_EQ(descriptor.namespaceName, "Gameplay"); EXPECT_EQ(descriptor.className, "RenderPipelineApiProbeAsset"); @@ -428,9 +437,9 @@ TEST_F( engine->OnUpdate(0.016f); const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = - XCEngine::Rendering::Pipelines::GetManagedRenderPipelineAssetDescriptor(); + XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor(); EXPECT_EQ(descriptor.assemblyName, "XCEngine.ScriptCore"); - EXPECT_EQ(descriptor.namespaceName, "XCEngine"); + EXPECT_EQ(descriptor.namespaceName, "XCEngine.Rendering.FirstParty"); EXPECT_EQ(descriptor.className, "ForwardRenderPipelineAsset"); EXPECT_NE(descriptor.managedAssetHandle, 0u); @@ -448,14 +457,14 @@ TEST_F( selectedPipelineAssetTypeName)); EXPECT_EQ( selectedPipelineAssetTypeName, - "XCEngine.ForwardRenderPipelineAsset"); + "XCEngine.Rendering.FirstParty.ForwardRenderPipelineAsset"); EXPECT_TRUE(runtime->TryGetFieldValue( script, "SelectedRendererDataTypeName", selectedRendererDataTypeName)); EXPECT_EQ( selectedRendererDataTypeName, - "XCEngine.ForwardRendererData"); + "XCEngine.Rendering.FirstParty.ForwardRendererData"); } TEST_F( @@ -477,7 +486,7 @@ TEST_F( configuredDescriptor.assemblyName = "GameScripts"; configuredDescriptor.namespaceName = "Gameplay"; configuredDescriptor.className = "RenderPipelineApiProbeAsset"; - XCEngine::Rendering::Pipelines::SetManagedRenderPipelineAssetDescriptor( + XCEngine::Rendering::Pipelines::SetConfiguredManagedRenderPipelineAssetDescriptor( configuredDescriptor); engine->OnRuntimeStart(runtimeScene); @@ -485,7 +494,7 @@ TEST_F( const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor resolvedDescriptor = - XCEngine::Rendering::Pipelines::GetManagedRenderPipelineAssetDescriptor(); + XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor(); EXPECT_EQ(resolvedDescriptor.assemblyName, configuredDescriptor.assemblyName); EXPECT_EQ(resolvedDescriptor.namespaceName, configuredDescriptor.namespaceName); EXPECT_EQ(resolvedDescriptor.className, configuredDescriptor.className); @@ -524,7 +533,7 @@ TEST_F( engine->OnUpdate(0.016f); const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptorBeforeStop = - XCEngine::Rendering::Pipelines::GetManagedRenderPipelineAssetDescriptor(); + XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor(); ASSERT_TRUE(descriptorBeforeStop.IsValid()); ASSERT_NE(descriptorBeforeStop.managedAssetHandle, 0u); ASSERT_NE( @@ -535,7 +544,7 @@ TEST_F( engine->OnRuntimeStop(); const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptorAfterStop = - XCEngine::Rendering::Pipelines::GetManagedRenderPipelineAssetDescriptor(); + XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor(); EXPECT_FALSE(descriptorAfterStop.IsValid()); EXPECT_EQ(descriptorAfterStop.managedAssetHandle, 0u); EXPECT_EQ( @@ -599,7 +608,7 @@ TEST_F( engine->OnUpdate(0.016f); const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptorBeforeReplace = - XCEngine::Rendering::Pipelines::GetManagedRenderPipelineAssetDescriptor(); + XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor(); ASSERT_TRUE(descriptorBeforeReplace.IsValid()); ASSERT_NE(descriptorBeforeReplace.managedAssetHandle, 0u); MonoObject* const managedAssetBeforeReplace = @@ -612,7 +621,7 @@ TEST_F( scene = std::move(secondScene); const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptorAfterReplace = - XCEngine::Rendering::Pipelines::GetManagedRenderPipelineAssetDescriptor(); + XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor(); EXPECT_EQ(descriptorAfterReplace.assemblyName, descriptorBeforeReplace.assemblyName); EXPECT_EQ(descriptorAfterReplace.namespaceName, descriptorBeforeReplace.namespaceName); EXPECT_EQ(descriptorAfterReplace.className, descriptorBeforeReplace.className); @@ -765,7 +774,9 @@ TEST_F( renderer.GetPipelineAsset()); ASSERT_NE(asset, nullptr); EXPECT_EQ(asset->GetDescriptor().assemblyName, "XCEngine.ScriptCore"); - EXPECT_EQ(asset->GetDescriptor().namespaceName, "XCEngine"); + EXPECT_EQ( + asset->GetDescriptor().namespaceName, + "XCEngine.Rendering.FirstParty"); EXPECT_EQ(asset->GetDescriptor().className, "ForwardRenderPipelineAsset"); auto* host = @@ -1015,7 +1026,7 @@ TEST_F( renderer.GetPipelineAsset()); ASSERT_NE(pipelineAsset, nullptr); EXPECT_EQ(pipelineAsset->GetDescriptor().assemblyName, "XCEngine.ScriptCore"); - EXPECT_EQ(pipelineAsset->GetDescriptor().namespaceName, "XCEngine"); + EXPECT_EQ(pipelineAsset->GetDescriptor().namespaceName, "XCEngine.Rendering.FirstParty"); EXPECT_EQ( pipelineAsset->GetDescriptor().className, "ForwardRenderPipelineAsset"); @@ -1091,7 +1102,7 @@ TEST_F( renderer.GetPipelineAsset()); ASSERT_NE(pipelineAsset, nullptr); EXPECT_EQ(pipelineAsset->GetDescriptor().assemblyName, "XCEngine.ScriptCore"); - EXPECT_EQ(pipelineAsset->GetDescriptor().namespaceName, "XCEngine"); + EXPECT_EQ(pipelineAsset->GetDescriptor().namespaceName, "XCEngine.Rendering.FirstParty"); EXPECT_EQ( pipelineAsset->GetDescriptor().className, "ForwardRenderPipelineAsset"); @@ -5381,3 +5392,4 @@ TEST_F(MonoScriptRuntimeTest, DisableEnablePreservesSingleInstanceAndStartOnlyRu } } // namespace +