Align SRP selection with asset refs

This commit is contained in:
2026-04-27 17:09:31 +08:00
parent c3f62c1fae
commit 2a7f6d0b60
9 changed files with 255 additions and 10 deletions

View File

@@ -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";
}
}

View File

@@ -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、classretained managed
object handle。Managed descriptors 存在 `GraphicsSettingsState`
- `ManagedRenderPipelineAssetDescriptor` 标识 managed asset assembly、namespace、classretained 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 cachemanaged materialization 更新 cache 时保留 configured asset reference。
- Managed SRP execution 由 `ScriptableRenderPipelineHost` 承载,它组合 native backend 和可选 managed
stage recorder。
- Mono-backed SRP assets 使用 `DefaultNativeBackend` 做 scene drawing并把 managed stages 记录到 native

View File

@@ -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<const Pipelines::ManagedRenderPipelineBridge>
m_managedRenderPipelineBridge = nullptr;
size_t m_managedRenderPipelineBridgeGeneration = 1u;

View File

@@ -1,5 +1,6 @@
#pragma once
#include <XCEngine/Core/Asset/AssetRef.h>
#include <XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h>
#include <cstdint>
@@ -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<const RenderPipelineAsset>

View File

@@ -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();
}

View File

@@ -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<const RenderPipelineAsset>
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<ManagedScriptableRenderPipelineAsset>(descriptor);
}

View File

@@ -4936,7 +4936,7 @@ MonoObject* InternalCall_Rendering_GetRenderPipelineAsset() {
}
Rendering::GetGraphicsSettingsState()
.SetConfiguredRenderPipelineAssetDescriptor(descriptor);
.UpdateConfiguredRenderPipelineAssetRuntimeDescriptor(descriptor);
}
return runtime->GetExternalManagedObject(

View File

@@ -55,6 +55,15 @@ CreateBuiltinForwardPipelineRendererAssetForTest() {
return std::make_shared<Pipelines::BuiltinForwardPipelineAsset>();
}
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<MockManagedRenderPipelineBridgeState>();
Pipelines::SetManagedRenderPipelineBridge(
std::make_shared<MockManagedRenderPipelineBridge>(bridgeState));
Pipelines::ManagedScriptableRenderPipelineAsset asset(descriptor);
std::unique_ptr<RenderPipeline> 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) {

View File

@@ -1,11 +1,13 @@
#include <gtest/gtest.h>
#include <XCEngine/Core/Asset/AssetRef.h>
#include <XCEngine/Rendering/GraphicsSettingsState.h>
#include <memory>
#include <utility>
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<const Pipelines::ManagedRenderPipelineBridge> 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();