rendering: close renderer override bridge phase 4.4
This commit is contained in:
@@ -98,6 +98,8 @@
|
||||
|
||||
### 4.4 稳住 renderer authoring / invalidation / bridge refresh
|
||||
|
||||
状态: `Completed` (`2026-04-26`)
|
||||
|
||||
目标: 把当前已经存在但还容易漂移的 authoring 路径变成可回归验证的稳定面。
|
||||
|
||||
执行:
|
||||
|
||||
@@ -2747,6 +2747,20 @@ private:
|
||||
|
||||
namespace {
|
||||
|
||||
std::string ResolveManagedMonoClassAssemblyName(MonoClass* monoClass) {
|
||||
if (monoClass == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
MonoImage* const image =
|
||||
mono_class_get_image(monoClass);
|
||||
return TrimAssemblyName(
|
||||
SafeString(
|
||||
image != nullptr
|
||||
? mono_image_get_name(image)
|
||||
: nullptr));
|
||||
}
|
||||
|
||||
ManagedComponentTypeInfo ResolveManagedComponentTypeInfo(MonoClass* monoClass) {
|
||||
ManagedComponentTypeInfo typeInfo;
|
||||
if (!monoClass) {
|
||||
@@ -2783,13 +2797,17 @@ ManagedComponentTypeInfo ResolveManagedComponentTypeInfo(MonoClass* monoClass) {
|
||||
}
|
||||
|
||||
MonoScriptRuntime* runtime = GetActiveMonoScriptRuntime();
|
||||
const std::string assemblyName =
|
||||
ResolveManagedMonoClassAssemblyName(
|
||||
monoClass);
|
||||
if (runtime
|
||||
&& !assemblyName.empty()
|
||||
&& runtime->IsClassAvailable(
|
||||
runtime->GetSettings().appAssemblyName,
|
||||
assemblyName,
|
||||
typeInfo.namespaceName,
|
||||
typeInfo.className)) {
|
||||
typeInfo.kind = ManagedComponentKind::Script;
|
||||
typeInfo.assemblyName = runtime->GetSettings().appAssemblyName;
|
||||
typeInfo.assemblyName = assemblyName;
|
||||
}
|
||||
|
||||
return typeInfo;
|
||||
@@ -7596,6 +7614,12 @@ bool MonoScriptRuntime::DiscoverScriptClasses() {
|
||||
}
|
||||
|
||||
DiscoverScriptClassesInImage(m_settings.appAssemblyName, m_appImage);
|
||||
for (const ManagedAssemblyDescriptor& assembly :
|
||||
m_settings.engineAssemblies) {
|
||||
DiscoverScriptClassesInImage(
|
||||
assembly.name,
|
||||
FindLoadedAssemblyImage(assembly.name));
|
||||
}
|
||||
DiscoverRenderPipelineAssetClassesInImage(
|
||||
m_settings.coreAssemblyName,
|
||||
m_coreImage);
|
||||
@@ -8748,11 +8772,14 @@ bool MonoScriptRuntime::TrySetFieldValue(
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string componentAssemblyName =
|
||||
ResolveManagedMonoClassAssemblyName(
|
||||
fieldMetadata.componentClass);
|
||||
ScriptComponent* component = FindScriptComponentByUUID(reference.scriptComponentUUID);
|
||||
if (!component
|
||||
|| !component->GetGameObject()
|
||||
|| component->GetGameObject()->GetUUID() != reference.gameObjectUUID
|
||||
|| component->GetAssemblyName() != m_settings.appAssemblyName
|
||||
|| component->GetAssemblyName() != componentAssemblyName
|
||||
|| component->GetNamespaceName() != SafeString(mono_class_get_namespace(fieldMetadata.componentClass))
|
||||
|| component->GetClassName() != SafeString(mono_class_get_name(fieldMetadata.componentClass))) {
|
||||
return false;
|
||||
|
||||
@@ -1852,6 +1852,42 @@ namespace Gameplay
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ManagedCameraOverrideRendererSelectionProbeAsset
|
||||
: UniversalRenderPipelineAsset
|
||||
{
|
||||
public ManagedCameraOverrideRendererSelectionProbeAsset()
|
||||
{
|
||||
rendererDataList =
|
||||
ProbeScriptableObjectFactory
|
||||
.CreateRendererDataList(
|
||||
ProbeScriptableObjectFactory
|
||||
.Create<ManagedRenderPipelineProbeRendererData>(),
|
||||
ProbeScriptableObjectFactory
|
||||
.Create<ManagedRenderPipelineProbeRendererData>());
|
||||
defaultRendererIndex = 0;
|
||||
}
|
||||
|
||||
protected override ScriptableRenderPipeline
|
||||
CreateRendererBackedPipeline()
|
||||
{
|
||||
return new ManagedCameraOverrideRendererSelectionProbePipeline();
|
||||
}
|
||||
|
||||
protected override void ConfigureCameraFramePlan(
|
||||
ScriptableRenderPipelinePlanningContext context)
|
||||
{
|
||||
if (context == null ||
|
||||
context.rendererIndex != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.RequestFullscreenStage(
|
||||
CameraFrameStage.PostProcess,
|
||||
CameraFrameColorSource.MainSceneColor);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ManagedRendererReuseProbeAsset
|
||||
: UniversalRenderPipelineAsset
|
||||
{
|
||||
@@ -2242,6 +2278,34 @@ namespace Gameplay
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ManagedCameraOverrideRendererSelectionProbePipeline
|
||||
: ScriptableRenderPipeline
|
||||
{
|
||||
protected override bool SupportsStageRenderGraphContextual(
|
||||
CameraFrameStage stage,
|
||||
int rendererIndex)
|
||||
{
|
||||
return stage == CameraFrameStage.PostProcess &&
|
||||
rendererIndex == 1;
|
||||
}
|
||||
|
||||
protected override bool RecordStageRenderGraph(
|
||||
ScriptableRenderContext context)
|
||||
{
|
||||
return context != null &&
|
||||
context.stage == CameraFrameStage.PostProcess &&
|
||||
context.rendererIndex == 1 &&
|
||||
context
|
||||
.AddRasterPass(
|
||||
"ManagedCameraOverridePostProcess")
|
||||
.SetColorAttachment(
|
||||
context.primaryColorTarget)
|
||||
.SetColorScaleFullscreenExecution(
|
||||
new Vector4(1.05f, 1.0f, 0.95f, 1.0f))
|
||||
.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ManagedRenderPipelineProbe
|
||||
: ProbeSceneRenderer
|
||||
{
|
||||
|
||||
@@ -3168,6 +3168,186 @@ TEST_F(
|
||||
host->GetStageRecorder()->Shutdown();
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
MonoScriptRuntimeTest,
|
||||
ManagedRenderPipelineBridgeUsesCameraRendererOverrideAcrossPlanningAndExecution) {
|
||||
const auto bridge =
|
||||
XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge();
|
||||
ASSERT_NE(bridge, nullptr);
|
||||
|
||||
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
|
||||
"GameScripts",
|
||||
"Gameplay",
|
||||
"ManagedCameraOverrideRendererSelectionProbeAsset"
|
||||
};
|
||||
|
||||
std::shared_ptr<const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetRuntime>
|
||||
assetRuntime = bridge->CreateAssetRuntime(descriptor);
|
||||
ASSERT_NE(assetRuntime, nullptr);
|
||||
EXPECT_EQ(
|
||||
assetRuntime->GetPipelineRendererAssetPolicy(),
|
||||
XCEngine::Rendering::Pipelines::ManagedPipelineRendererAssetPolicy::
|
||||
DefaultNativeBackend);
|
||||
|
||||
Scene* runtimeScene =
|
||||
CreateScene("ManagedCameraOverrideRendererSelectionScene");
|
||||
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
ASSERT_NE(camera, nullptr);
|
||||
camera->SetPrimary(true);
|
||||
|
||||
ScriptComponent* additionalCameraData =
|
||||
cameraObject->AddComponent<ScriptComponent>();
|
||||
ASSERT_NE(additionalCameraData, nullptr);
|
||||
additionalCameraData->SetScriptClass(
|
||||
"XCEngine.RenderPipelines.Universal",
|
||||
"XCEngine.Rendering.Universal",
|
||||
"UniversalAdditionalCameraData");
|
||||
additionalCameraData->GetFieldStorage().SetFieldValue(
|
||||
"rendererIndex",
|
||||
int32_t(1));
|
||||
|
||||
engine->OnRuntimeStart(runtimeScene);
|
||||
engine->OnUpdate(0.016f);
|
||||
|
||||
TestRenderDevice device;
|
||||
TestRenderCommandList commandList;
|
||||
TestRenderCommandQueue commandQueue;
|
||||
TestRenderResourceView colorView(
|
||||
XCEngine::RHI::ResourceViewType::RenderTarget,
|
||||
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
||||
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
||||
TestRenderResourceView depthView(
|
||||
XCEngine::RHI::ResourceViewType::DepthStencil,
|
||||
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
||||
XCEngine::RHI::Format::D32_Float);
|
||||
|
||||
const XCEngine::Rendering::RenderContext context =
|
||||
CreateRenderContext(
|
||||
device,
|
||||
commandList,
|
||||
commandQueue);
|
||||
XCEngine::Rendering::RenderSurface surface(64u, 64u);
|
||||
surface.SetColorAttachment(&colorView);
|
||||
surface.SetDepthAttachment(&depthView);
|
||||
|
||||
XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset
|
||||
asset(descriptor);
|
||||
|
||||
XCEngine::Rendering::CameraRenderRequest request = {};
|
||||
request.scene = runtimeScene;
|
||||
request.camera = camera;
|
||||
request.context = context;
|
||||
request.surface = surface;
|
||||
asset.ConfigureCameraRenderRequest(
|
||||
request,
|
||||
0u,
|
||||
0u,
|
||||
XCEngine::Rendering::DirectionalShadowPlanningSettings{});
|
||||
EXPECT_EQ(request.rendererIndex, 1);
|
||||
|
||||
XCEngine::Rendering::CameraFramePlan plan =
|
||||
XCEngine::Rendering::CameraFramePlan::FromRequest(request);
|
||||
asset.ConfigureCameraFramePlan(plan);
|
||||
EXPECT_EQ(plan.request.rendererIndex, 1);
|
||||
EXPECT_TRUE(
|
||||
plan.IsFullscreenStageRequested(
|
||||
XCEngine::Rendering::CameraFrameStage::PostProcess));
|
||||
EXPECT_EQ(
|
||||
plan.ResolveStageColorSource(
|
||||
XCEngine::Rendering::CameraFrameStage::PostProcess),
|
||||
XCEngine::Rendering::CameraFrameColorSource::MainSceneColor);
|
||||
EXPECT_TRUE(plan.IsPostProcessStageValid());
|
||||
|
||||
std::unique_ptr<XCEngine::Rendering::RenderPipeline> pipeline =
|
||||
asset.CreatePipeline();
|
||||
ASSERT_NE(pipeline, nullptr);
|
||||
auto* host =
|
||||
dynamic_cast<XCEngine::Rendering::Pipelines::ScriptableRenderPipelineHost*>(
|
||||
pipeline.get());
|
||||
ASSERT_NE(host, nullptr);
|
||||
ASSERT_NE(host->GetStageRecorder(), nullptr);
|
||||
EXPECT_TRUE(host->GetStageRecorder()->Initialize(context))
|
||||
<< runtime->GetLastError();
|
||||
EXPECT_FALSE(
|
||||
host->GetStageRecorder()->SupportsStageRenderGraph(
|
||||
XCEngine::Rendering::RenderPipelineStageSupportContext{
|
||||
XCEngine::Rendering::CameraFrameStage::PostProcess,
|
||||
0 }));
|
||||
EXPECT_TRUE(
|
||||
host->GetStageRecorder()->SupportsStageRenderGraph(
|
||||
XCEngine::Rendering::RenderPipelineStageSupportContext{
|
||||
XCEngine::Rendering::CameraFrameStage::PostProcess,
|
||||
plan.request.rendererIndex }));
|
||||
|
||||
XCEngine::Rendering::RenderGraph graph;
|
||||
XCEngine::Rendering::RenderGraphBuilder graphBuilder(graph);
|
||||
XCEngine::Rendering::RenderGraphTextureDesc postProcessColorDesc = {};
|
||||
postProcessColorDesc.width = 64u;
|
||||
postProcessColorDesc.height = 64u;
|
||||
postProcessColorDesc.format =
|
||||
static_cast<XCEngine::Core::uint32>(
|
||||
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
||||
const XCEngine::Rendering::RenderGraphTextureHandle sourceColor =
|
||||
graphBuilder.ImportTexture(
|
||||
"ManagedCameraOverrideSource",
|
||||
postProcessColorDesc,
|
||||
&colorView,
|
||||
{});
|
||||
const XCEngine::Rendering::RenderGraphTextureHandle outputColor =
|
||||
graphBuilder.CreateTransientTexture(
|
||||
"ManagedCameraOverrideOutput",
|
||||
postProcessColorDesc);
|
||||
|
||||
const XCEngine::Rendering::RenderSceneData sceneData = {};
|
||||
bool executionSucceeded = true;
|
||||
XCEngine::Rendering::RenderGraphBlackboard blackboard = {};
|
||||
XCEngine::Rendering::EmplaceCameraFrameRenderGraphFrameData(blackboard)
|
||||
.resources.mainScene.color = sourceColor;
|
||||
XCEngine::Rendering::RenderPipelineStageRenderGraphContext graphContext = {
|
||||
graphBuilder,
|
||||
"ManagedCameraOverridePostProcess",
|
||||
XCEngine::Rendering::CameraFrameStage::PostProcess,
|
||||
context,
|
||||
sceneData,
|
||||
surface,
|
||||
nullptr,
|
||||
nullptr,
|
||||
XCEngine::RHI::ResourceStates::Common,
|
||||
sourceColor,
|
||||
{ outputColor },
|
||||
{},
|
||||
{},
|
||||
&executionSucceeded,
|
||||
&blackboard
|
||||
};
|
||||
graphContext.stageColorSource =
|
||||
plan.ResolveStageColorSource(
|
||||
XCEngine::Rendering::CameraFrameStage::PostProcess);
|
||||
graphContext.usesGraphManagedOutputColor =
|
||||
plan.UsesGraphManagedOutputColor(
|
||||
XCEngine::Rendering::CameraFrameStage::PostProcess);
|
||||
graphContext.rendererIndex = plan.request.rendererIndex;
|
||||
|
||||
EXPECT_TRUE(host->GetStageRecorder()->RecordStageRenderGraph(graphContext))
|
||||
<< runtime->GetLastError();
|
||||
|
||||
XCEngine::Rendering::CompiledRenderGraph compiledGraph = {};
|
||||
XCEngine::Containers::String errorMessage;
|
||||
ASSERT_TRUE(
|
||||
XCEngine::Rendering::RenderGraphCompiler::Compile(
|
||||
graph,
|
||||
compiledGraph,
|
||||
&errorMessage))
|
||||
<< errorMessage.CStr();
|
||||
ASSERT_EQ(compiledGraph.GetPassCount(), 1u);
|
||||
EXPECT_STREQ(
|
||||
compiledGraph.GetPassName(0).CStr(),
|
||||
"ManagedCameraOverridePostProcess");
|
||||
|
||||
host->GetStageRecorder()->Shutdown();
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
MonoScriptRuntimeTest,
|
||||
ManagedRenderPipelineBridgeFallsBackToDefaultSceneRecorderWhenBackendKeyIsUnknown) {
|
||||
|
||||
Reference in New Issue
Block a user