Serialize managed SRP asset graphs

This commit is contained in:
2026-04-27 19:35:17 +08:00
parent 8353da05e5
commit c0b670b052
10 changed files with 1568 additions and 25 deletions

View File

@@ -109,7 +109,8 @@ Unity 兼容的公开命名、对象所有权和扩展点。
- `GraphicsSettings.renderPipelineAsset == null` 表示 renderer 使用 engine default native pipeline selection。
- `GraphicsSettings.renderPipelineAsset != null` 表示 renderer 使用 render-pipeline asset reference selection path。
`AssetRef` 是长期选择身份managed descriptor/handle 只是运行时 materialization cache 和过渡 fallback。
`AssetRef` 是长期选择身份managed descriptor 中的 serialized ScriptableObject graph 和 handle 只是运行时
materialization cache 和过渡 fallback。
- Runtime startup 不应静默指定 project default SRP asset。Project 或 editor policy 可以显式选择一个,
但 active rendering mode 必须能通过 `GraphicsSettings.renderPipelineAsset` 观察到。
- `GraphicsSettingsState` 同时保存 configured render-pipeline asset `AssetRef`
@@ -198,11 +199,16 @@ Native passes 仍用于 backend fallback、工具和 built-in rendering。
Managed SRP assets 通过 `GraphicsSettings.renderPipelineAsset` 选择,并通过
`ManagedScriptableRenderPipelineAsset` bridge。
- `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。
- `ManagedRenderPipelineAssetDescriptor` 标识 managed asset assembly、namespace、class、可选 `AssetRef`
serialized ScriptableObject asset graph 和 retained managed object handle。它不再是 configured pipeline
的唯一身份;`GraphicsSettingsState` 中的 configured `AssetRef` 才是 Unity 风格 asset selection root。
- Descriptor-only selection 仍保留给测试、legacy runtime fallback 和尚未接入 asset import 的路径。没有
`serializedAssetGraph` 时才允许按 class 默认构造 materialize一旦 snapshot 存在,缺失或失效的
managed object handle 必须从 snapshot 重建,不能退回 code-created default asset。
- `GraphicsSettings.renderPipelineAsset = asset` 必须把当前 managed SRP asset graph snapshot 收进 descriptor。
该 snapshot 覆盖 `UniversalRenderPipelineAsset -> ScriptableRendererData -> ScriptableRendererFeature`
以及 public/`[SerializeField]` serializable settings。Runtime cache 更新可以替换 handle但不得丢失
snapshot 或 configured `AssetRef`
- `ManagedScriptableRenderPipelineAsset` 解析 `ManagedRenderPipelineAssetRuntime`,创建
`ScriptableRenderPipelineHost`,并把 request/plan/final-color policy calls 转发给 managed code。
- `ScriptableRenderPipelineHost` 组合 native backend asset 和可选 managed stage recorder。当 managed recorder
@@ -339,8 +345,8 @@ Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。
authoring 还未公开。
- `UniversalPostProcessBlock` 仍保留 post-process source promotion helper实际 post-process stage 由
active pass queue 中的 features/passes 声明。
- Render-pipeline selection 已切到 `AssetRef` 作为根身份,`UniversalRendererData`、features 和
`ScriptableObject` 字段/子资产仍是 code-created objects还不是完整 Unity 风格 serialized asset pipeline
- Render-pipeline selection 已切到 `AssetRef` 作为根身份,并且 managed SRP descriptor 已保存
ScriptableObject graph snapshot完整 editor/importer `.asset` 持久化和 sub-asset authoring 仍未完成
- 当前 shadow support 是单个 main directional shadow path没有 cascades。
- Graph compiler/executor 当前没有实现 pass culling 或 transient aliasing。
@@ -354,6 +360,9 @@ Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。
`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 descriptor 已接入 serialized ScriptableObject graph snapshot。`GraphicsSettings` setter 保存
当前 asset/data/features 图getter 和 `MonoManagedRenderPipelineAssetRuntime` 在 handle 缺失或失效时按
snapshot 重建;无 snapshot 的 descriptor-only selection 才保留 class 默认构造 fallback。
- Managed SRP execution 由 `ScriptableRenderPipelineHost` 承载,它组合 native backend 和可选 managed
stage recorder。
- Mono-backed SRP assets 使用 `DefaultNativeBackend` 做 scene drawing并把 managed stages 记录到 native

View File

@@ -33,6 +33,7 @@ struct ManagedRenderPipelineAssetDescriptor {
std::string className;
uint32_t managedAssetHandle = 0u;
Resources::AssetRef assetRef = {};
std::string serializedAssetGraph;
bool IsValid() const {
return !assemblyName.empty() && !className.empty();
@@ -46,6 +47,10 @@ struct ManagedRenderPipelineAssetDescriptor {
return assetRef.IsValid();
}
bool HasSerializedAssetGraph() const {
return !serializedAssetGraph.empty();
}
std::string GetFullName() const {
return namespaceName.empty()
? className

View File

@@ -103,6 +103,12 @@ public:
const char* passName,
uint64_t commandBufferHandle);
bool IsScriptableRenderPipelineAssetObject(MonoObject* managedObject) const;
bool TrySerializeManagedRenderPipelineAssetGraph(
MonoObject* managedObject,
std::string& outSerializedGraph);
bool TryCreateManagedRenderPipelineAssetFromSerializedGraph(
const std::string& serializedGraph,
uint32_t& outHandle);
bool TryEnsureManagedRenderPipelineAssetHandle(
Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor& ioDescriptor);
@@ -266,6 +272,9 @@ private:
bool CreateExternalManagedObject(
MonoClass* monoClass,
uint32_t& outHandle);
bool TryDeserializeManagedRenderPipelineAssetGraph(
const std::string& serializedGraph,
MonoObject*& outAsset);
uint32_t RetainExternalManagedObject(MonoObject* instance);
void DestroyExternalManagedObject(uint32_t gcHandle);
MonoObject* CreateManagedScriptableRenderContext(uint64_t nativeHandle);

View File

@@ -2752,24 +2752,33 @@ bool MonoManagedRenderPipelineAssetRuntime::EnsureManagedAsset() const {
? mono_object_get_class(assetObject)
: nullptr;
if (assetClass == nullptr) {
m_runtime->SetError(
"Managed render pipeline asset handle is no longer valid: " +
m_descriptor.GetFullName() + ".");
return false;
}
if (!IsMonoClassOrSubclass(
assetClass,
m_runtime->m_scriptableRenderPipelineAssetClass)) {
if (!m_descriptor.HasSerializedAssetGraph()) {
m_runtime->SetError(
"Managed render pipeline asset handle is no longer valid: " +
m_descriptor.GetFullName() + ".");
return false;
}
} else if (!IsMonoClassOrSubclass(
assetClass,
m_runtime->m_scriptableRenderPipelineAssetClass)) {
m_runtime->SetError(
"Managed render pipeline asset must derive from ScriptableRenderPipelineAsset: " +
m_descriptor.GetFullName() + ".");
return false;
} else {
m_assetHandle = m_descriptor.managedAssetHandle;
m_ownsManagedAssetHandle = false;
return true;
}
}
m_assetHandle = m_descriptor.managedAssetHandle;
m_ownsManagedAssetHandle = false;
return true;
if (m_descriptor.HasSerializedAssetGraph()) {
m_ownsManagedAssetHandle =
m_runtime->TryCreateManagedRenderPipelineAssetFromSerializedGraph(
m_descriptor.serializedAssetGraph,
m_assetHandle) &&
m_assetHandle != 0;
return m_ownsManagedAssetHandle;
}
MonoClass* assetClass = nullptr;
@@ -4896,6 +4905,17 @@ void InternalCall_Rendering_SetRenderPipelineAsset(MonoObject* assetObject) {
runtime != nullptr
? runtime->RetainExternalManagedObjectReference(assetObject)
: 0u;
if (runtime != nullptr &&
!runtime->TrySerializeManagedRenderPipelineAssetGraph(
assetObject,
descriptor.serializedAssetGraph)) {
if (descriptor.managedAssetHandle != 0u) {
runtime->ReleaseExternalManagedObject(
descriptor.managedAssetHandle);
}
return;
}
if (!descriptor.IsValid() ||
descriptor.managedAssetHandle == 0u) {
if (runtime != nullptr &&
@@ -4930,11 +4950,13 @@ MonoObject* InternalCall_Rendering_GetRenderPipelineAsset() {
return nullptr;
}
if (descriptor.managedAssetHandle == 0u) {
if (!runtime->TryEnsureManagedRenderPipelineAssetHandle(descriptor)) {
return nullptr;
}
const uint32_t previousManagedAssetHandle =
descriptor.managedAssetHandle;
if (!runtime->TryEnsureManagedRenderPipelineAssetHandle(descriptor)) {
return nullptr;
}
if (descriptor.managedAssetHandle != previousManagedAssetHandle) {
Rendering::GetGraphicsSettingsState()
.UpdateConfiguredRenderPipelineAssetRuntimeDescriptor(descriptor);
}
@@ -8531,6 +8553,127 @@ bool MonoScriptRuntime::IsScriptableRenderPipelineAssetObject(
m_scriptableRenderPipelineAssetClass);
}
bool MonoScriptRuntime::TrySerializeManagedRenderPipelineAssetGraph(
MonoObject* managedObject,
std::string& outSerializedGraph) {
outSerializedGraph.clear();
if (!m_initialized ||
!IsScriptableRenderPipelineAssetObject(managedObject) ||
m_scriptableRenderPipelineAssetClass == nullptr) {
return false;
}
MonoMethod* const serializeMethod =
ResolveManagedMethod(
m_scriptableRenderPipelineAssetClass,
"SerializeAssetGraphInstance",
1);
if (serializeMethod == nullptr) {
SetError(
"Managed ScriptableRenderPipelineAsset.SerializeAssetGraphInstance was not found.");
return false;
}
SetCurrentDomain();
void* args[1] = { managedObject };
MonoObject* exception = nullptr;
MonoObject* const result =
mono_runtime_invoke(
serializeMethod,
nullptr,
args,
&exception);
if (exception != nullptr) {
RecordException(exception);
return false;
}
MonoString* const serializedGraph =
reinterpret_cast<MonoString*>(result);
outSerializedGraph = MonoStringToUtf8(serializedGraph);
if (outSerializedGraph.empty()) {
SetError(
"Managed render pipeline asset graph serialization returned an empty snapshot.");
return false;
}
return true;
}
bool MonoScriptRuntime::TryDeserializeManagedRenderPipelineAssetGraph(
const std::string& serializedGraph,
MonoObject*& outAsset) {
outAsset = nullptr;
if (!m_initialized ||
serializedGraph.empty() ||
m_scriptableRenderPipelineAssetClass == nullptr) {
return false;
}
MonoMethod* const deserializeMethod =
ResolveManagedMethod(
m_scriptableRenderPipelineAssetClass,
"DeserializeAssetGraphInstance",
1);
if (deserializeMethod == nullptr) {
SetError(
"Managed ScriptableRenderPipelineAsset.DeserializeAssetGraphInstance was not found.");
return false;
}
SetCurrentDomain();
MonoString* const managedSerializedGraph =
mono_string_new(
m_appDomain,
serializedGraph.c_str());
void* args[1] = { managedSerializedGraph };
MonoObject* exception = nullptr;
MonoObject* const result =
mono_runtime_invoke(
deserializeMethod,
nullptr,
args,
&exception);
if (exception != nullptr) {
RecordException(exception);
return false;
}
if (!IsScriptableRenderPipelineAssetObject(result)) {
SetError(
"Managed render pipeline asset graph deserialization did not return a ScriptableRenderPipelineAsset.");
return false;
}
outAsset = result;
return true;
}
bool MonoScriptRuntime::TryCreateManagedRenderPipelineAssetFromSerializedGraph(
const std::string& serializedGraph,
uint32_t& outHandle) {
outHandle = 0u;
MonoObject* assetObject = nullptr;
if (!TryDeserializeManagedRenderPipelineAssetGraph(
serializedGraph,
assetObject) ||
assetObject == nullptr) {
return false;
}
outHandle = RetainExternalManagedObject(assetObject);
if (outHandle == 0u) {
SetError(
"Managed render pipeline asset graph deserialization returned an object that could not be retained.");
return false;
}
return true;
}
bool MonoScriptRuntime::TryEnsureManagedRenderPipelineAssetHandle(
Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor& ioDescriptor) {
if (!m_initialized || !ioDescriptor.IsValid()) {
@@ -8547,6 +8690,37 @@ bool MonoScriptRuntime::TryEnsureManagedRenderPipelineAssetHandle(
ioDescriptor.managedAssetHandle = 0u;
}
if (ioDescriptor.HasSerializedAssetGraph()) {
if (!TryCreateManagedRenderPipelineAssetFromSerializedGraph(
ioDescriptor.serializedAssetGraph,
ioDescriptor.managedAssetHandle) ||
ioDescriptor.managedAssetHandle == 0u) {
return false;
}
MonoObject* const assetObject =
GetExternalManagedObject(ioDescriptor.managedAssetHandle);
MonoClass* const assetClass =
assetObject != nullptr
? mono_object_get_class(assetObject)
: nullptr;
if (assetClass != nullptr) {
MonoImage* const image =
mono_class_get_image(assetClass);
ioDescriptor.assemblyName =
TrimAssemblyName(
SafeString(
image != nullptr
? mono_image_get_name(image)
: nullptr));
ioDescriptor.namespaceName =
SafeString(mono_class_get_namespace(assetClass));
ioDescriptor.className =
SafeString(mono_class_get_name(assetClass));
}
return true;
}
MonoClass* assetClass = nullptr;
if (!ResolveManagedClass(
ioDescriptor.assemblyName,

View File

@@ -162,6 +162,7 @@ set(XCENGINE_SCRIPT_CORE_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/MonoBehaviour.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Object.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScriptableObject.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/ScriptableObjectSerializedGraph.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Physics.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/PhysicsBodyType.cs
${CMAKE_CURRENT_SOURCE_DIR}/XCEngine.ScriptCore/Quaternion.cs

View File

@@ -3314,6 +3314,135 @@ namespace Gameplay
}
}
public sealed class SerializedUniversalRenderPipelineAssetGraphProbe
: MonoBehaviour
{
public int UpdateCount;
public bool ObservedAssetWasNull = true;
public int ObservedDefaultRendererIndex = -1;
public int ObservedRendererDataCount;
public int ObservedRendererFeatureCount;
public bool ObservedFeatureActive = true;
public string ObservedFeatureTypeName = string.Empty;
public Vector4 ObservedColorScale;
public bool ObservedShadowsSupportMainLight = true;
public Vector4 ObservedFinalColorScale;
public void Start()
{
UniversalRenderPipelineAsset asset =
ScriptableObject
.CreateInstance<UniversalRenderPipelineAsset>();
UniversalRendererData rendererData =
ScriptableObject
.CreateInstance<UniversalRendererData>();
ColorScalePostProcessRendererFeature colorScaleFeature =
ScriptableObject
.CreateInstance<ColorScalePostProcessRendererFeature>();
if (asset == null ||
rendererData == null ||
colorScaleFeature == null)
{
return;
}
colorScaleFeature.colorScale = new Vector4(
0.42f,
0.51f,
0.63f,
1.0f);
colorScaleFeature.SetActive(false);
rendererData.rendererFeatures =
new ScriptableRendererFeature[]
{
colorScaleFeature
};
asset.rendererDataList =
new ScriptableRendererData[]
{
rendererData
};
asset.defaultRendererIndex = 0;
asset.shadows =
UniversalShadowSettings.CreateDefault();
asset.shadows.supportsMainLightShadows = false;
asset.finalColor =
UniversalFinalColorSettings.CreateDefault();
asset.finalColor.settings.finalColorScale =
new Vector4(
0.90f,
1.10f,
1.20f,
1.0f);
GraphicsSettings.renderPipelineAsset = asset;
}
public void Update()
{
++UpdateCount;
if (UpdateCount < 2)
{
return;
}
UniversalRenderPipelineAsset asset =
GraphicsSettings.renderPipelineAsset as
UniversalRenderPipelineAsset;
ObservedAssetWasNull = asset == null;
if (asset == null)
{
return;
}
ObservedDefaultRendererIndex =
asset.defaultRendererIndex;
ObservedRendererDataCount =
asset.rendererDataList != null
? asset.rendererDataList.Length
: 0;
ObservedShadowsSupportMainLight =
asset.shadows != null &&
asset.shadows.supportsMainLightShadows;
ObservedFinalColorScale =
asset.finalColor != null
? asset.finalColor.settings.finalColorScale
: new Vector4();
ScriptableRendererData rendererData =
asset.rendererDataList != null &&
asset.rendererDataList.Length > 0
? asset.rendererDataList[0]
: null;
ObservedRendererFeatureCount =
rendererData != null &&
rendererData.rendererFeatures != null
? rendererData.rendererFeatures.Length
: 0;
ScriptableRendererFeature feature =
rendererData != null &&
rendererData.rendererFeatures != null &&
rendererData.rendererFeatures.Length > 0
? rendererData.rendererFeatures[0]
: null;
ObservedFeatureTypeName =
feature != null
? feature.GetType().FullName ?? string.Empty
: string.Empty;
ObservedFeatureActive =
feature != null &&
feature.isActive;
ColorScalePostProcessRendererFeature colorScaleFeature =
feature as ColorScalePostProcessRendererFeature;
ObservedColorScale =
colorScaleFeature != null
? colorScaleFeature.colorScale
: new Vector4();
}
}
public sealed class ScriptCoreUniversalShadowlessRendererFeatureRuntimeSelectionProbe
: MonoBehaviour
{

View File

@@ -10,7 +10,7 @@ namespace XCEngine.Rendering.Universal
{
private bool m_disposed;
private bool m_runtimeCreated;
private bool m_isActive = true;
[SerializeField] private bool m_isActive = true;
private int m_runtimeStateVersion = 1;
private int m_runtimeStateHash;
private bool m_runtimeStateHashResolved;

View File

@@ -10,6 +10,21 @@ namespace XCEngine.Rendering
{
}
internal static string SerializeAssetGraphInstance(
ScriptableRenderPipelineAsset asset)
{
return ScriptableObjectSerializedGraph.Serialize(asset);
}
internal static ScriptableRenderPipelineAsset
DeserializeAssetGraphInstance(
string serializedAssetGraph)
{
return ScriptableObjectSerializedGraph
.Deserialize(serializedAssetGraph) as
ScriptableRenderPipelineAsset;
}
internal void ReleaseRuntimeResourcesInstance()
{
ReleaseRuntimeResources();

File diff suppressed because it is too large Load Diff

View File

@@ -529,6 +529,156 @@ TEST_F(
"Gameplay.RenderPipelineApiProbeAsset");
}
TEST_F(
MonoScriptRuntimeTest,
ManagedGraphicsSettingsMaterializesSerializedUniversalAssetGraphWithoutExistingHandle) {
Scene* runtimeScene =
CreateScene("SerializedUniversalRenderPipelineAssetGraphScene");
GameObject* scriptObject =
runtimeScene->CreateGameObject(
"SerializedUniversalRenderPipelineAssetGraphProbe");
ScriptComponent* script =
AddScript(
scriptObject,
"Gameplay",
"SerializedUniversalRenderPipelineAssetGraphProbe");
ASSERT_NE(script, nullptr);
engine->OnRuntimeStart(runtimeScene);
engine->OnUpdate(0.016f);
XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor
descriptor =
XCEngine::Rendering::Pipelines::
GetConfiguredManagedRenderPipelineAssetDescriptor();
ASSERT_TRUE(descriptor.IsValid());
ASSERT_EQ(
descriptor.assemblyName,
"XCEngine.RenderPipelines.Universal");
ASSERT_EQ(
descriptor.namespaceName,
"XCEngine.Rendering.Universal");
ASSERT_EQ(
descriptor.className,
"UniversalRenderPipelineAsset");
ASSERT_NE(descriptor.managedAssetHandle, 0u);
ASSERT_FALSE(descriptor.serializedAssetGraph.empty());
runtime->ReleaseExternalManagedObject(descriptor.managedAssetHandle);
descriptor.managedAssetHandle = 0u;
XCEngine::Rendering::Pipelines::
SetConfiguredManagedRenderPipelineAssetDescriptor(descriptor);
{
const auto bridge =
XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge();
ASSERT_NE(bridge, nullptr);
std::shared_ptr<const XCEngine::Rendering::Pipelines::
ManagedRenderPipelineAssetRuntime>
assetRuntime = bridge->CreateAssetRuntime(descriptor);
ASSERT_NE(assetRuntime, nullptr);
XCEngine::Rendering::FinalColorSettings settings = {};
ASSERT_TRUE(assetRuntime->TryGetDefaultFinalColorSettings(settings));
EXPECT_FLOAT_EQ(settings.finalColorScale.x, 0.90f);
EXPECT_FLOAT_EQ(settings.finalColorScale.y, 1.10f);
EXPECT_FLOAT_EQ(settings.finalColorScale.z, 1.20f);
EXPECT_FLOAT_EQ(settings.finalColorScale.w, 1.0f);
std::unique_ptr<XCEngine::Rendering::RenderPipelineStageRecorder>
recorder = assetRuntime->CreateStageRecorder();
ASSERT_NE(recorder, nullptr);
const XCEngine::Rendering::RenderContext context = {};
ASSERT_TRUE(recorder->Initialize(context));
EXPECT_TRUE(
recorder->SupportsStageRenderGraph(
XCEngine::Rendering::CameraFrameStage::MainScene));
EXPECT_FALSE(
recorder->SupportsStageRenderGraph(
XCEngine::Rendering::CameraFrameStage::PostProcess));
recorder->Shutdown();
}
engine->OnUpdate(0.016f);
engine->OnUpdate(0.016f);
EXPECT_TRUE(runtime->GetLastError().empty()) << runtime->GetLastError();
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor
resolvedDescriptor =
XCEngine::Rendering::Pipelines::
GetConfiguredManagedRenderPipelineAssetDescriptor();
EXPECT_NE(resolvedDescriptor.managedAssetHandle, 0u);
EXPECT_FALSE(resolvedDescriptor.serializedAssetGraph.empty());
int updateCount = 0;
bool observedAssetWasNull = true;
int observedDefaultRendererIndex = -1;
int observedRendererDataCount = 0;
int observedRendererFeatureCount = 0;
bool observedFeatureActive = true;
std::string observedFeatureTypeName;
XCEngine::Math::Vector4 observedColorScale = {};
bool observedShadowsSupportMainLight = true;
XCEngine::Math::Vector4 observedFinalColorScale = {};
EXPECT_TRUE(runtime->TryGetFieldValue(script, "UpdateCount", updateCount));
EXPECT_TRUE(runtime->TryGetFieldValue(
script,
"ObservedAssetWasNull",
observedAssetWasNull));
EXPECT_TRUE(runtime->TryGetFieldValue(
script,
"ObservedDefaultRendererIndex",
observedDefaultRendererIndex));
EXPECT_TRUE(runtime->TryGetFieldValue(
script,
"ObservedRendererDataCount",
observedRendererDataCount));
EXPECT_TRUE(runtime->TryGetFieldValue(
script,
"ObservedRendererFeatureCount",
observedRendererFeatureCount));
EXPECT_TRUE(runtime->TryGetFieldValue(
script,
"ObservedFeatureActive",
observedFeatureActive));
EXPECT_TRUE(runtime->TryGetFieldValue(
script,
"ObservedFeatureTypeName",
observedFeatureTypeName));
EXPECT_TRUE(runtime->TryGetFieldValue(
script,
"ObservedColorScale",
observedColorScale));
EXPECT_TRUE(runtime->TryGetFieldValue(
script,
"ObservedShadowsSupportMainLight",
observedShadowsSupportMainLight));
EXPECT_TRUE(runtime->TryGetFieldValue(
script,
"ObservedFinalColorScale",
observedFinalColorScale));
EXPECT_GE(updateCount, 2);
EXPECT_FALSE(observedAssetWasNull);
EXPECT_EQ(observedDefaultRendererIndex, 0);
EXPECT_EQ(observedRendererDataCount, 1);
EXPECT_EQ(observedRendererFeatureCount, 1);
EXPECT_FALSE(observedFeatureActive);
EXPECT_EQ(
observedFeatureTypeName,
"XCEngine.Rendering.Universal.ColorScalePostProcessRendererFeature");
EXPECT_FLOAT_EQ(observedColorScale.x, 0.42f);
EXPECT_FLOAT_EQ(observedColorScale.y, 0.51f);
EXPECT_FLOAT_EQ(observedColorScale.z, 0.63f);
EXPECT_FLOAT_EQ(observedColorScale.w, 1.0f);
EXPECT_FALSE(observedShadowsSupportMainLight);
EXPECT_FLOAT_EQ(observedFinalColorScale.x, 0.90f);
EXPECT_FLOAT_EQ(observedFinalColorScale.y, 1.10f);
EXPECT_FLOAT_EQ(observedFinalColorScale.z, 1.20f);
EXPECT_FLOAT_EQ(observedFinalColorScale.w, 1.0f);
}
TEST_F(
MonoScriptRuntimeTest,
RuntimeStopClearsManagedGraphicsSettingsSelection) {