diff --git a/engine/include/XCEngine/Core/Asset/ResourceTypes.h b/engine/include/XCEngine/Core/Asset/ResourceTypes.h index 2d9e04a3..fe33cfb1 100644 --- a/engine/include/XCEngine/Core/Asset/ResourceTypes.h +++ b/engine/include/XCEngine/Core/Asset/ResourceTypes.h @@ -28,7 +28,8 @@ enum class ResourceType : Core::uint8 { UISchema, VolumeField, Model, - GaussianSplat + GaussianSplat, + RenderPipelineAsset }; constexpr const char* GetResourceTypeName(ResourceType type) { @@ -51,6 +52,7 @@ constexpr const char* GetResourceTypeName(ResourceType type) { case ResourceType::VolumeField: return "VolumeField"; case ResourceType::Model: return "Model"; case ResourceType::GaussianSplat: return "GaussianSplat"; + case ResourceType::RenderPipelineAsset: return "RenderPipelineAsset"; default: return "Unknown"; } } diff --git a/engine/include/XCEngine/Rendering/AGENTS.md b/engine/include/XCEngine/Rendering/AGENTS.md index e442749b..d2759c77 100644 --- a/engine/include/XCEngine/Rendering/AGENTS.md +++ b/engine/include/XCEngine/Rendering/AGENTS.md @@ -104,9 +104,13 @@ Unity 兼容的公开命名、对象所有权和扩展点。 ### 管线选择 - `GraphicsSettings.renderPipelineAsset == null` 表示 renderer 使用 engine default native pipeline selection。 -- `GraphicsSettings.renderPipelineAsset != null` 表示 renderer 使用 managed SRP asset descriptor path。 +- `GraphicsSettings.renderPipelineAsset != null` 表示 renderer 使用 render-pipeline asset reference selection path。 + `AssetRef` 是长期选择身份;managed descriptor/handle 只是运行时 materialization cache 和过渡 fallback。 - Runtime startup 不应静默指定 project default SRP asset。Project 或 editor policy 可以显式选择一个, 但 active rendering mode 必须能通过 `GraphicsSettings.renderPipelineAsset` 观察到。 +- `GraphicsSettingsState` 同时保存 configured render-pipeline asset `AssetRef` 和 + `ManagedRenderPipelineAssetDescriptor` runtime cache。只要 `AssetRef` 存在,它就是选择根;后续 managed + materialization 只能更新 descriptor cache,不得清掉或替换该 asset reference。 - 如果 managed asset 无法创建有效 pipeline、recorder 或 backend bridge,应显式失败,并让 top-level factory 选择 fallback path。不要让 `ScriptableRenderPipelineHost` 看起来像成功的 managed SRP,同时在背后用 hidden native path 渲染不受支持的 managed stage。 @@ -186,8 +190,11 @@ Native passes 仍用于 backend fallback、工具和 built-in rendering。 Managed SRP assets 通过 `GraphicsSettings.renderPipelineAsset` 选择,并通过 `ManagedScriptableRenderPipelineAsset` bridge。 -- `ManagedRenderPipelineAssetDescriptor` 标识 managed asset assembly、namespace、class 和 retained managed - object handle。Managed descriptors 存在 `GraphicsSettingsState` 中。 +- `ManagedRenderPipelineAssetDescriptor` 标识 managed asset assembly、namespace、class、retained managed + object handle 和可选 `AssetRef`。它不再是 configured pipeline 的唯一身份;`GraphicsSettingsState` 中的 + configured `AssetRef` 才是 Unity 风格 asset selection root。 +- Descriptor-only selection 仍保留给测试、legacy runtime fallback 和尚未接入 asset serialization 的路径。 + 一旦已有 configured `AssetRef`,不要把 managed object handle 或 descriptor 当作长期 asset identity。 - `ManagedScriptableRenderPipelineAsset` 解析 `ManagedRenderPipelineAssetRuntime`,创建 `ScriptableRenderPipelineHost`,并把 request/plan/final-color policy calls 转发给 managed code。 - `ScriptableRenderPipelineHost` 组合 native backend asset 和可选 managed stage recorder。当 managed recorder @@ -324,7 +331,8 @@ Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。 authoring 还未公开。 - `UniversalPostProcessBlock` 仍保留 post-process source promotion helper;实际 post-process stage 由 active pass queue 中的 features/passes 声明。 -- `UniversalRendererData` 和 features 是 code-created objects,还不是完整 Unity 风格 serialized asset pipeline。 +- Render-pipeline selection 已切到 `AssetRef` 作为根身份,但 `UniversalRendererData`、features 和 + `ScriptableObject` 字段/子资产仍是 code-created objects,还不是完整 Unity 风格 serialized asset pipeline。 - 当前 shadow support 是单个 main directional shadow path,没有 cascades。 - Graph compiler/executor 当前没有实现 pass culling 或 transient aliasing。 @@ -336,6 +344,8 @@ Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。 - `GraphicsSettings.renderPipelineAsset` 已通过 `ManagedScriptableRenderPipelineAsset` 和 `GraphicsSettingsState` 选择 managed SRP assets。 +- Render-pipeline asset selection 已从 descriptor/managed handle 切到 `AssetRef` 根身份;descriptor 保留为 + assembly/type/handle runtime cache,managed materialization 更新 cache 时保留 configured asset reference。 - Managed SRP execution 由 `ScriptableRenderPipelineHost` 承载,它组合 native backend 和可选 managed stage recorder。 - Mono-backed SRP assets 使用 `DefaultNativeBackend` 做 scene drawing,并把 managed stages 记录到 native diff --git a/engine/include/XCEngine/Rendering/GraphicsSettingsState.h b/engine/include/XCEngine/Rendering/GraphicsSettingsState.h index 963b3057..e6f257d2 100644 --- a/engine/include/XCEngine/Rendering/GraphicsSettingsState.h +++ b/engine/include/XCEngine/Rendering/GraphicsSettingsState.h @@ -27,7 +27,17 @@ public: void SetConfiguredRenderPipelineAssetDescriptor( const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor); + void SetConfiguredRenderPipelineAssetRef( + const Resources::AssetRef& assetRef); + void SetConfiguredRenderPipelineAssetSelection( + const Resources::AssetRef& assetRef, + const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor); + void UpdateConfiguredRenderPipelineAssetRuntimeDescriptor( + const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor); void ClearConfiguredRenderPipelineAssetDescriptor(); + Resources::AssetRef GetConfiguredRenderPipelineAssetRef() const { + return m_configuredRenderPipelineAssetRef; + } Pipelines::ManagedRenderPipelineAssetDescriptor GetConfiguredRenderPipelineAssetDescriptor() const { return m_configuredRenderPipelineAssetDescriptor; @@ -38,6 +48,7 @@ private: Pipelines::ManagedRenderPipelineAssetDescriptor m_configuredRenderPipelineAssetDescriptor = {}; + Resources::AssetRef m_configuredRenderPipelineAssetRef = {}; 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 7ca667b5..52c1dfd6 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h +++ b/engine/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -31,6 +32,7 @@ struct ManagedRenderPipelineAssetDescriptor { std::string namespaceName; std::string className; uint32_t managedAssetHandle = 0u; + Resources::AssetRef assetRef = {}; bool IsValid() const { return !assemblyName.empty() && !className.empty(); @@ -40,6 +42,10 @@ struct ManagedRenderPipelineAssetDescriptor { return managedAssetHandle != 0u; } + bool HasAssetRef() const { + return assetRef.IsValid(); + } + std::string GetFullName() const { return namespaceName.empty() ? className @@ -150,7 +156,15 @@ size_t GetManagedRenderPipelineEnvironmentGeneration(); void SetConfiguredManagedRenderPipelineAssetDescriptor( const ManagedRenderPipelineAssetDescriptor& descriptor); +void SetConfiguredManagedRenderPipelineAssetRef( + const Resources::AssetRef& assetRef); +void SetConfiguredManagedRenderPipelineAssetSelection( + const Resources::AssetRef& assetRef, + const ManagedRenderPipelineAssetDescriptor& descriptor); +void UpdateConfiguredManagedRenderPipelineAssetRuntimeDescriptor( + const ManagedRenderPipelineAssetDescriptor& descriptor); void ClearConfiguredManagedRenderPipelineAssetDescriptor(); +Resources::AssetRef GetConfiguredManagedRenderPipelineAssetRef(); ManagedRenderPipelineAssetDescriptor GetConfiguredManagedRenderPipelineAssetDescriptor(); std::shared_ptr diff --git a/engine/src/Rendering/GraphicsSettingsState.cpp b/engine/src/Rendering/GraphicsSettingsState.cpp index 2965c3a1..7a4f0d03 100644 --- a/engine/src/Rendering/GraphicsSettingsState.cpp +++ b/engine/src/Rendering/GraphicsSettingsState.cpp @@ -26,11 +26,53 @@ void GraphicsSettingsState::ClearManagedRenderPipelineBridge() { void GraphicsSettingsState::SetConfiguredRenderPipelineAssetDescriptor( const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor) { m_configuredRenderPipelineAssetDescriptor = descriptor; + m_configuredRenderPipelineAssetRef = descriptor.assetRef; + if (!descriptor.assetRef.IsValid()) { + m_configuredRenderPipelineAssetRef.Reset(); + } + BumpEnvironmentGeneration(); +} + +void GraphicsSettingsState::SetConfiguredRenderPipelineAssetRef( + const Resources::AssetRef& assetRef) { + m_configuredRenderPipelineAssetRef = assetRef; + m_configuredRenderPipelineAssetDescriptor = {}; + if (m_configuredRenderPipelineAssetRef.IsValid()) { + m_configuredRenderPipelineAssetDescriptor.assetRef = + m_configuredRenderPipelineAssetRef; + } + BumpEnvironmentGeneration(); +} + +void GraphicsSettingsState::SetConfiguredRenderPipelineAssetSelection( + const Resources::AssetRef& assetRef, + const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor) { + m_configuredRenderPipelineAssetRef = assetRef; + m_configuredRenderPipelineAssetDescriptor = descriptor; + if (m_configuredRenderPipelineAssetRef.IsValid()) { + m_configuredRenderPipelineAssetDescriptor.assetRef = + m_configuredRenderPipelineAssetRef; + } else if (descriptor.assetRef.IsValid()) { + m_configuredRenderPipelineAssetRef = descriptor.assetRef; + } + BumpEnvironmentGeneration(); +} + +void GraphicsSettingsState::UpdateConfiguredRenderPipelineAssetRuntimeDescriptor( + const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor) { + m_configuredRenderPipelineAssetDescriptor = descriptor; + if (descriptor.assetRef.IsValid()) { + m_configuredRenderPipelineAssetRef = descriptor.assetRef; + } else if (m_configuredRenderPipelineAssetRef.IsValid()) { + m_configuredRenderPipelineAssetDescriptor.assetRef = + m_configuredRenderPipelineAssetRef; + } BumpEnvironmentGeneration(); } void GraphicsSettingsState::ClearConfiguredRenderPipelineAssetDescriptor() { m_configuredRenderPipelineAssetDescriptor = {}; + m_configuredRenderPipelineAssetRef.Reset(); BumpEnvironmentGeneration(); } diff --git a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp index 330040e5..9a0e39de 100644 --- a/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp +++ b/engine/src/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.cpp @@ -193,10 +193,36 @@ void SetConfiguredManagedRenderPipelineAssetDescriptor( descriptor); } +void SetConfiguredManagedRenderPipelineAssetRef( + const Resources::AssetRef& assetRef) { + GetGraphicsSettingsState().SetConfiguredRenderPipelineAssetRef( + assetRef); +} + +void SetConfiguredManagedRenderPipelineAssetSelection( + const Resources::AssetRef& assetRef, + const ManagedRenderPipelineAssetDescriptor& descriptor) { + GetGraphicsSettingsState().SetConfiguredRenderPipelineAssetSelection( + assetRef, + descriptor); +} + +void UpdateConfiguredManagedRenderPipelineAssetRuntimeDescriptor( + const ManagedRenderPipelineAssetDescriptor& descriptor) { + GetGraphicsSettingsState() + .UpdateConfiguredRenderPipelineAssetRuntimeDescriptor( + descriptor); +} + void ClearConfiguredManagedRenderPipelineAssetDescriptor() { GetGraphicsSettingsState().ClearConfiguredRenderPipelineAssetDescriptor(); } +Resources::AssetRef GetConfiguredManagedRenderPipelineAssetRef() { + return GetGraphicsSettingsState() + .GetConfiguredRenderPipelineAssetRef(); +} + ManagedRenderPipelineAssetDescriptor GetConfiguredManagedRenderPipelineAssetDescriptor() { return GetGraphicsSettingsState() @@ -205,8 +231,13 @@ GetConfiguredManagedRenderPipelineAssetDescriptor() { std::shared_ptr CreateConfiguredManagedRenderPipelineAsset() { - const ManagedRenderPipelineAssetDescriptor descriptor = + ManagedRenderPipelineAssetDescriptor descriptor = GetGraphicsSettingsState().GetConfiguredRenderPipelineAssetDescriptor(); + const Resources::AssetRef assetRef = + GetGraphicsSettingsState().GetConfiguredRenderPipelineAssetRef(); + if (assetRef.IsValid()) { + descriptor.assetRef = assetRef; + } if (descriptor.IsValid()) { return std::make_shared(descriptor); } diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index 6f153546..37b83481 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -4936,7 +4936,7 @@ MonoObject* InternalCall_Rendering_GetRenderPipelineAsset() { } Rendering::GetGraphicsSettingsState() - .SetConfiguredRenderPipelineAssetDescriptor(descriptor); + .UpdateConfiguredRenderPipelineAssetRuntimeDescriptor(descriptor); } return runtime->GetExternalManagedObject( diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index 97f5f7ec..54cba591 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -55,6 +55,15 @@ CreateBuiltinForwardPipelineRendererAssetForTest() { return std::make_shared(); } +XCEngine::Resources::AssetRef MakeRenderPipelineAssetRefForTest() { + XCEngine::Resources::AssetRef assetRef = {}; + assetRef.assetGuid = XCEngine::Resources::AssetGUID(11u, 29u); + assetRef.localID = XCEngine::Resources::kMainAssetLocalID; + assetRef.resourceType = + XCEngine::Resources::ResourceType::RenderPipelineAsset; + return assetRef; +} + class ScopedPipelineRendererAssetFactoryRegistration final { public: ScopedPipelineRendererAssetFactoryRegistration( @@ -4761,6 +4770,41 @@ TEST(ManagedScriptableRenderPipelineAsset_Test, CreatesHostWithStageRecorderFrom Pipelines::ClearManagedRenderPipelineBridge(); } +TEST( + ManagedScriptableRenderPipelineAsset_Test, + PassesConfiguredAssetRefToManagedBridgeAsRuntimeCacheIdentity) { + Pipelines::ClearManagedRenderPipelineBridge(); + + Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { + "GameScripts", + "Gameplay", + "ManagedRenderPipelineProbeAsset" + }; + const XCEngine::Resources::AssetRef assetRef = + MakeRenderPipelineAssetRefForTest(); + descriptor.assetRef = assetRef; + + auto bridgeState = std::make_shared(); + Pipelines::SetManagedRenderPipelineBridge( + std::make_shared(bridgeState)); + + Pipelines::ManagedScriptableRenderPipelineAsset asset(descriptor); + std::unique_ptr pipeline = asset.CreatePipeline(); + ASSERT_NE(pipeline, nullptr); + ASSERT_TRUE(bridgeState->lastDescriptor.assetRef.IsValid()); + EXPECT_EQ( + bridgeState->lastDescriptor.assetRef.assetGuid, + assetRef.assetGuid); + EXPECT_EQ( + bridgeState->lastDescriptor.assetRef.localID, + assetRef.localID); + EXPECT_EQ( + bridgeState->lastDescriptor.assetRef.resourceType, + assetRef.resourceType); + + Pipelines::ClearManagedRenderPipelineBridge(); +} + TEST( ManagedScriptableRenderPipelineAsset_Test, UsesRuntimeProvidedPipelineRendererAssetForHostComposition) { diff --git a/tests/Rendering/unit/test_graphics_settings_state.cpp b/tests/Rendering/unit/test_graphics_settings_state.cpp index 49018bd1..65a3d25c 100644 --- a/tests/Rendering/unit/test_graphics_settings_state.cpp +++ b/tests/Rendering/unit/test_graphics_settings_state.cpp @@ -1,11 +1,13 @@ #include +#include #include #include #include using namespace XCEngine::Rendering; +namespace Resources = XCEngine::Resources; namespace { @@ -19,6 +21,9 @@ public: : m_savedDescriptor( GetGraphicsSettingsState() .GetConfiguredRenderPipelineAssetDescriptor()) + , m_savedAssetRef( + GetGraphicsSettingsState() + .GetConfiguredRenderPipelineAssetRef()) , m_savedBridge( GetGraphicsSettingsState().GetManagedRenderPipelineBridge()) { } @@ -31,9 +36,14 @@ public: state.ClearManagedRenderPipelineBridge(); } - if (m_savedDescriptor.IsValid() || - m_savedDescriptor.managedAssetHandle != 0u) { - state.SetConfiguredRenderPipelineAssetDescriptor(m_savedDescriptor); + if (m_savedAssetRef.IsValid()) { + state.SetConfiguredRenderPipelineAssetSelection( + m_savedAssetRef, + m_savedDescriptor); + } else if (m_savedDescriptor.IsValid() || + m_savedDescriptor.managedAssetHandle != 0u) { + state.SetConfiguredRenderPipelineAssetDescriptor( + m_savedDescriptor); } else { state.ClearConfiguredRenderPipelineAssetDescriptor(); } @@ -41,10 +51,20 @@ public: private: Pipelines::ManagedRenderPipelineAssetDescriptor m_savedDescriptor = {}; + Resources::AssetRef m_savedAssetRef = {}; std::shared_ptr m_savedBridge = nullptr; }; +Resources::AssetRef MakeRenderPipelineAssetRefForTest( + uint64_t guidLow = 2u) { + Resources::AssetRef assetRef = {}; + assetRef.assetGuid = Resources::AssetGUID(1u, guidLow); + assetRef.localID = Resources::kMainAssetLocalID; + assetRef.resourceType = Resources::ResourceType::RenderPipelineAsset; + return assetRef; +} + } // namespace TEST(GraphicsSettingsState_Test, StoresRenderPipelineAssetDescriptorAndBumpsEnvironmentGeneration) { @@ -68,9 +88,80 @@ TEST(GraphicsSettingsState_Test, StoresRenderPipelineAssetDescriptorAndBumpsEnvi EXPECT_EQ(resolved.namespaceName, descriptor.namespaceName); EXPECT_EQ(resolved.className, descriptor.className); EXPECT_EQ(resolved.managedAssetHandle, descriptor.managedAssetHandle); + EXPECT_FALSE(state.GetConfiguredRenderPipelineAssetRef().IsValid()); EXPECT_GT(state.GetEnvironmentGeneration(), generationBefore); } +TEST( + GraphicsSettingsState_Test, + StoresRenderPipelineAssetRefAsSelectionRootAndDescriptorAsRuntimeCache) { + GraphicsSettingsStateScope scope; + GraphicsSettingsState& state = GetGraphicsSettingsState(); + state.ClearConfiguredRenderPipelineAssetDescriptor(); + + const Resources::AssetRef assetRef = + MakeRenderPipelineAssetRefForTest(); + const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = { + "XCEngine.RenderPipelines.Universal", + "XCEngine.Rendering.Universal", + "UniversalRenderPipelineAsset", + 0u + }; + + const size_t generationBefore = state.GetEnvironmentGeneration(); + state.SetConfiguredRenderPipelineAssetSelection( + assetRef, + descriptor); + + const Resources::AssetRef resolvedAssetRef = + state.GetConfiguredRenderPipelineAssetRef(); + EXPECT_EQ(resolvedAssetRef.assetGuid, assetRef.assetGuid); + EXPECT_EQ(resolvedAssetRef.localID, assetRef.localID); + EXPECT_EQ(resolvedAssetRef.resourceType, assetRef.resourceType); + + const Pipelines::ManagedRenderPipelineAssetDescriptor resolvedDescriptor = + state.GetConfiguredRenderPipelineAssetDescriptor(); + EXPECT_EQ(resolvedDescriptor.assemblyName, descriptor.assemblyName); + EXPECT_EQ(resolvedDescriptor.namespaceName, descriptor.namespaceName); + EXPECT_EQ(resolvedDescriptor.className, descriptor.className); + EXPECT_TRUE(resolvedDescriptor.assetRef.IsValid()); + EXPECT_EQ(resolvedDescriptor.assetRef.assetGuid, assetRef.assetGuid); + EXPECT_GT(state.GetEnvironmentGeneration(), generationBefore); +} + +TEST( + GraphicsSettingsState_Test, + RuntimeDescriptorUpdatePreservesConfiguredRenderPipelineAssetRef) { + GraphicsSettingsStateScope scope; + GraphicsSettingsState& state = GetGraphicsSettingsState(); + state.ClearConfiguredRenderPipelineAssetDescriptor(); + + const Resources::AssetRef assetRef = + MakeRenderPipelineAssetRefForTest(3u); + state.SetConfiguredRenderPipelineAssetRef(assetRef); + + Pipelines::ManagedRenderPipelineAssetDescriptor runtimeDescriptor = { + "GameScripts", + "Gameplay", + "ProjectConfiguredRenderPipelineAsset", + 77u + }; + state.UpdateConfiguredRenderPipelineAssetRuntimeDescriptor( + runtimeDescriptor); + + const Resources::AssetRef resolvedAssetRef = + state.GetConfiguredRenderPipelineAssetRef(); + EXPECT_EQ(resolvedAssetRef.assetGuid, assetRef.assetGuid); + EXPECT_EQ(resolvedAssetRef.localID, assetRef.localID); + EXPECT_EQ(resolvedAssetRef.resourceType, assetRef.resourceType); + + const Pipelines::ManagedRenderPipelineAssetDescriptor resolvedDescriptor = + state.GetConfiguredRenderPipelineAssetDescriptor(); + EXPECT_EQ(resolvedDescriptor.managedAssetHandle, 77u); + EXPECT_TRUE(resolvedDescriptor.assetRef.IsValid()); + EXPECT_EQ(resolvedDescriptor.assetRef.assetGuid, assetRef.assetGuid); +} + TEST(GraphicsSettingsState_Test, StoresManagedBridgeAndBumpsBridgeAndEnvironmentGenerations) { GraphicsSettingsStateScope scope; GraphicsSettingsState& state = GetGraphicsSettingsState();