refactor(rendering): add srp host stage recorder bridge

This commit is contained in:
2026-04-15 20:07:52 +08:00
parent 22a2b982ef
commit cafe3c8076
6 changed files with 283 additions and 5 deletions

View File

@@ -416,6 +416,15 @@ struct MockPipelineAssetState {
FinalColorSettings defaultFinalColorSettings = {};
};
struct MockStageRecorderState {
int initializeCalls = 0;
int shutdownCalls = 0;
int recordMainSceneCalls = 0;
bool supportsMainSceneRenderGraph = false;
bool recordMainSceneResult = true;
bool lastReceivedRenderGraphBlackboard = false;
};
class MockPipeline final : public RenderPipeline {
public:
explicit MockPipeline(std::shared_ptr<MockPipelineState> state)
@@ -592,6 +601,37 @@ private:
std::shared_ptr<MockPipelineState> m_state;
};
class MockStageRecorder final : public RenderPipelineStageRecorder {
public:
explicit MockStageRecorder(std::shared_ptr<MockStageRecorderState> state)
: m_state(std::move(state)) {
}
bool Initialize(const RenderContext&) override {
++m_state->initializeCalls;
return true;
}
void Shutdown() override {
++m_state->shutdownCalls;
}
bool SupportsStageRenderGraph(CameraFrameStage stage) const override {
return SupportsCameraFramePipelineGraphRecording(stage) &&
m_state->supportsMainSceneRenderGraph;
}
bool RecordStageRenderGraph(
const RenderPipelineStageRenderGraphContext& context) override {
++m_state->recordMainSceneCalls;
m_state->lastReceivedRenderGraphBlackboard = context.blackboard != nullptr;
return m_state->recordMainSceneResult;
}
private:
std::shared_ptr<MockStageRecorderState> m_state;
};
template <typename PassT, typename... Args>
PassT* InstallStandaloneStagePass(
RenderPipeline& pipeline,
@@ -623,6 +663,34 @@ private:
std::shared_ptr<MockPipelineAssetState> m_state;
};
struct MockManagedRenderPipelineBridgeState {
int createStageRecorderCalls = 0;
Pipelines::ManagedRenderPipelineAssetDescriptor lastDescriptor = {};
std::shared_ptr<MockStageRecorderState> lastCreatedStageRecorderState;
};
class MockManagedRenderPipelineBridge final
: public Pipelines::ManagedRenderPipelineBridge {
public:
explicit MockManagedRenderPipelineBridge(
std::shared_ptr<MockManagedRenderPipelineBridgeState> state)
: m_state(std::move(state)) {
}
std::unique_ptr<RenderPipelineStageRecorder> CreateStageRecorder(
const Pipelines::ManagedRenderPipelineAssetDescriptor& descriptor) const override {
++m_state->createStageRecorderCalls;
m_state->lastDescriptor = descriptor;
m_state->lastCreatedStageRecorderState =
std::make_shared<MockStageRecorderState>();
return std::make_unique<MockStageRecorder>(
m_state->lastCreatedStageRecorderState);
}
private:
std::shared_ptr<MockManagedRenderPipelineBridgeState> m_state;
};
class MockObjectIdPass final : public RenderPass {
public:
MockObjectIdPass(
@@ -3755,6 +3823,75 @@ TEST(ScriptableRenderPipelineHost_Test, ForwardsRendererLifetimeAndFrameRenderin
EXPECT_EQ(replacementState->shutdownCalls, 1);
}
TEST(ScriptableRenderPipelineHost_Test, PrefersStageRecorderBeforeFallbackRenderer) {
auto rendererState = std::make_shared<MockPipelineState>();
auto initialRecorderState = std::make_shared<MockStageRecorderState>();
auto replacementRecorderState = std::make_shared<MockStageRecorderState>();
rendererState->supportsMainSceneRenderGraph = true;
initialRecorderState->supportsMainSceneRenderGraph = true;
replacementRecorderState->supportsMainSceneRenderGraph = true;
{
Pipelines::ScriptableRenderPipelineHost host(
std::make_unique<MockPipeline>(rendererState));
auto initialRecorder =
std::make_unique<MockStageRecorder>(initialRecorderState);
MockStageRecorder* initialRecorderRaw = initialRecorder.get();
host.SetStageRecorder(std::move(initialRecorder));
EXPECT_EQ(host.GetStageRecorder(), initialRecorderRaw);
auto replacementRecorder =
std::make_unique<MockStageRecorder>(replacementRecorderState);
MockStageRecorder* replacementRecorderRaw = replacementRecorder.get();
host.SetStageRecorder(std::move(replacementRecorder));
EXPECT_EQ(initialRecorderState->shutdownCalls, 1);
EXPECT_EQ(host.GetStageRecorder(), replacementRecorderRaw);
Scene scene("ScriptableRenderPipelineRecorderScene");
GameObject* cameraObject = scene.CreateGameObject("Camera");
auto* camera = cameraObject->AddComponent<CameraComponent>();
camera->SetPrimary(true);
camera->SetDepth(1.0f);
const RenderContext context = CreateValidContext();
const RenderSurface surface(800, 600);
RenderSceneData sceneData =
CreateSceneDataForCamera(scene, *camera, surface);
RenderGraph graph = {};
RenderGraphBuilder graphBuilder(graph);
RenderGraphBlackboard blackboard = {};
bool executionSucceeded = true;
const RenderPipelineStageRenderGraphContext graphContext = {
graphBuilder,
"MainScene",
CameraFrameStage::MainScene,
context,
sceneData,
surface,
nullptr,
nullptr,
XCEngine::RHI::ResourceStates::Common,
{},
{},
{},
&executionSucceeded,
&blackboard
};
EXPECT_TRUE(host.SupportsStageRenderGraph(CameraFrameStage::MainScene));
EXPECT_TRUE(host.RecordStageRenderGraph(graphContext));
EXPECT_EQ(replacementRecorderState->recordMainSceneCalls, 1);
EXPECT_TRUE(replacementRecorderState->lastReceivedRenderGraphBlackboard);
EXPECT_EQ(rendererState->recordMainSceneCalls, 0);
}
EXPECT_EQ(rendererState->shutdownCalls, 1);
EXPECT_EQ(replacementRecorderState->shutdownCalls, 1);
}
TEST(ScriptableRenderPipelineHostAsset_Test, CreatesHostFromRendererAssetAndForwardsDefaults) {
auto assetState = std::make_shared<MockPipelineAssetState>();
assetState->defaultFinalColorSettings.outputTransferMode =
@@ -3784,6 +3921,41 @@ TEST(ScriptableRenderPipelineHostAsset_Test, CreatesHostFromRendererAssetAndForw
EXPECT_EQ(assetState->createCalls, 1);
}
TEST(ManagedScriptableRenderPipelineAsset_Test, CreatesHostWithStageRecorderFromManagedBridge) {
Pipelines::ClearManagedRenderPipelineBridge();
const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
"GameScripts",
"Gameplay",
"ManagedRenderPipelineProbeAsset"
};
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);
auto* host =
dynamic_cast<Pipelines::ScriptableRenderPipelineHost*>(pipeline.get());
ASSERT_NE(host, nullptr);
EXPECT_NE(host->GetPipelineRenderer(), nullptr);
EXPECT_NE(host->GetStageRecorder(), nullptr);
EXPECT_EQ(bridgeState->createStageRecorderCalls, 1);
EXPECT_EQ(bridgeState->lastDescriptor.assemblyName, "GameScripts");
EXPECT_EQ(bridgeState->lastDescriptor.namespaceName, "Gameplay");
EXPECT_EQ(bridgeState->lastDescriptor.className, "ManagedRenderPipelineProbeAsset");
ASSERT_NE(bridgeState->lastCreatedStageRecorderState, nullptr);
bridgeState->lastCreatedStageRecorderState->supportsMainSceneRenderGraph = true;
EXPECT_TRUE(host->SupportsStageRenderGraph(CameraFrameStage::MainScene));
}
Pipelines::ClearManagedRenderPipelineBridge();
}
TEST(CameraRenderer_Test, DefaultPipelineAssetUsesManagedSelectionWhenPresent) {
const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
"GameScripts",