fix(rendering): refresh managed pipelines on srp environment changes

This commit is contained in:
2026-04-18 01:10:45 +08:00
parent e388b6fbe3
commit df02a4e741
5 changed files with 140 additions and 3 deletions

View File

@@ -4,6 +4,7 @@
#include <XCEngine/Rendering/Extraction/RenderSceneExtractor.h>
#include <XCEngine/Rendering/RenderPipeline.h>
#include <cstddef>
#include <memory>
namespace XCEngine {
@@ -33,12 +34,14 @@ public:
void SetPipeline(std::unique_ptr<RenderPipeline> pipeline);
void SetPipelineAsset(std::shared_ptr<const RenderPipelineAsset> pipelineAsset);
RenderPipeline* GetPipeline() const { return m_pipeline.get(); }
const RenderPipelineAsset* GetPipelineAsset() const { return m_pipelineAsset.get(); }
RenderPipeline* GetPipeline() const;
const RenderPipelineAsset* GetPipelineAsset() const;
bool Render(const CameraFramePlan& plan);
private:
bool UsesManagedPipelineBinding() const;
void RefreshManagedPipelineBindingIfNeeded();
void ResetPipeline(std::unique_ptr<RenderPipeline> pipeline);
bool BuildSceneDataForPlan(
const CameraFramePlan& plan,
@@ -53,6 +56,8 @@ private:
std::shared_ptr<const RenderPipelineAsset> m_pipelineAsset;
std::unique_ptr<RenderPipeline> m_pipeline;
std::unique_ptr<DirectionalShadowRuntime> m_directionalShadowRuntime;
bool m_usesDefaultPipelineAssetSelection = false;
size_t m_managedPipelineEnvironmentGeneration = 0u;
};
} // namespace Rendering

View File

@@ -96,6 +96,7 @@ void SetManagedRenderPipelineBridge(
void ClearManagedRenderPipelineBridge();
std::shared_ptr<const ManagedRenderPipelineBridge>
GetManagedRenderPipelineBridge();
size_t GetManagedRenderPipelineEnvironmentGeneration();
void SetManagedRenderPipelineAssetDescriptor(
const ManagedRenderPipelineAssetDescriptor& descriptor);

View File

@@ -4,6 +4,7 @@
#include "Rendering/Execution/DirectionalShadowExecutionState.h"
#include "Rendering/Execution/Internal/CameraFrameGraph/Executor.h"
#include "Rendering/Internal/RenderPipelineFactory.h"
#include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h"
#include "Rendering/RenderPipelineAsset.h"
#include "Rendering/RenderSurface.h"
#include "Rendering/Shadow/DirectionalShadowRuntime.h"
@@ -21,10 +22,17 @@ bool RequiresPipelineStageRecording(
plan.GetPassSequence(stage) == nullptr;
}
bool IsManagedPipelineAsset(
const std::shared_ptr<const RenderPipelineAsset>& pipelineAsset) {
return dynamic_cast<const Pipelines::ManagedScriptableRenderPipelineAsset*>(
pipelineAsset.get()) != nullptr;
}
} // namespace
CameraRenderer::CameraRenderer()
: CameraRenderer(Internal::CreateDefaultRenderPipelineAsset()) {
: m_directionalShadowRuntime(std::make_unique<DirectionalShadowRuntime>()) {
SetPipelineAsset(nullptr);
}
CameraRenderer::CameraRenderer(std::unique_ptr<RenderPipeline> pipeline)
@@ -46,16 +54,53 @@ CameraRenderer::~CameraRenderer() {
void CameraRenderer::SetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
m_pipelineAsset.reset();
m_usesDefaultPipelineAssetSelection = false;
m_managedPipelineEnvironmentGeneration = 0u;
ResetPipeline(std::move(pipeline));
}
void CameraRenderer::SetPipelineAsset(std::shared_ptr<const RenderPipelineAsset> pipelineAsset) {
m_usesDefaultPipelineAssetSelection = pipelineAsset == nullptr;
ResetPipeline(
Internal::CreateRenderPipelineOrDefault(
pipelineAsset,
&m_pipelineAsset));
}
RenderPipeline* CameraRenderer::GetPipeline() const {
const_cast<CameraRenderer*>(this)->RefreshManagedPipelineBindingIfNeeded();
return m_pipeline.get();
}
const RenderPipelineAsset* CameraRenderer::GetPipelineAsset() const {
const_cast<CameraRenderer*>(this)->RefreshManagedPipelineBindingIfNeeded();
return m_pipelineAsset.get();
}
bool CameraRenderer::UsesManagedPipelineBinding() const {
return m_usesDefaultPipelineAssetSelection ||
IsManagedPipelineAsset(m_pipelineAsset);
}
void CameraRenderer::RefreshManagedPipelineBindingIfNeeded() {
if (!UsesManagedPipelineBinding()) {
return;
}
const size_t currentGeneration =
Pipelines::GetManagedRenderPipelineEnvironmentGeneration();
if (currentGeneration == m_managedPipelineEnvironmentGeneration) {
return;
}
if (m_usesDefaultPipelineAssetSelection) {
SetPipelineAsset(nullptr);
return;
}
SetPipelineAsset(m_pipelineAsset);
}
void CameraRenderer::ResetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
if (m_pipeline != nullptr) {
m_pipeline->Shutdown();
@@ -63,11 +108,17 @@ void CameraRenderer::ResetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
m_pipeline = std::move(pipeline);
if (m_pipeline == nullptr) {
m_usesDefaultPipelineAssetSelection = true;
m_pipeline =
Internal::CreateRenderPipelineOrDefault(
nullptr,
&m_pipelineAsset);
}
m_managedPipelineEnvironmentGeneration =
UsesManagedPipelineBinding()
? Pipelines::GetManagedRenderPipelineEnvironmentGeneration()
: 0u;
}
bool CameraRenderer::BuildSceneDataForPlan(
@@ -109,6 +160,8 @@ bool CameraRenderer::ExecuteRenderPlan(
bool CameraRenderer::Render(
const CameraFramePlan& plan) {
RefreshManagedPipelineBindingIfNeeded();
if (!plan.IsValid() || m_pipeline == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,

View File

@@ -23,6 +23,11 @@ size_t& GetManagedRenderPipelineBridgeGenerationStorage() {
return s_generation;
}
size_t& GetManagedRenderPipelineEnvironmentGenerationStorage() {
static size_t s_generation = 1u;
return s_generation;
}
} // namespace
ManagedScriptableRenderPipelineAsset::ManagedScriptableRenderPipelineAsset(
@@ -109,11 +114,13 @@ void SetManagedRenderPipelineBridge(
std::shared_ptr<const ManagedRenderPipelineBridge> bridge) {
GetManagedRenderPipelineBridgeStorage() = std::move(bridge);
++GetManagedRenderPipelineBridgeGenerationStorage();
++GetManagedRenderPipelineEnvironmentGenerationStorage();
}
void ClearManagedRenderPipelineBridge() {
GetManagedRenderPipelineBridgeStorage().reset();
++GetManagedRenderPipelineBridgeGenerationStorage();
++GetManagedRenderPipelineEnvironmentGenerationStorage();
}
std::shared_ptr<const ManagedRenderPipelineBridge>
@@ -121,13 +128,19 @@ GetManagedRenderPipelineBridge() {
return GetManagedRenderPipelineBridgeStorage();
}
size_t GetManagedRenderPipelineEnvironmentGeneration() {
return GetManagedRenderPipelineEnvironmentGenerationStorage();
}
void SetManagedRenderPipelineAssetDescriptor(
const ManagedRenderPipelineAssetDescriptor& descriptor) {
GetManagedRenderPipelineAssetDescriptorStorage() = descriptor;
++GetManagedRenderPipelineEnvironmentGenerationStorage();
}
void ClearManagedRenderPipelineAssetDescriptor() {
GetManagedRenderPipelineAssetDescriptorStorage() = {};
++GetManagedRenderPipelineEnvironmentGenerationStorage();
}
ManagedRenderPipelineAssetDescriptor GetManagedRenderPipelineAssetDescriptor() {

View File

@@ -4912,6 +4912,37 @@ TEST(CameraRenderer_Test, DefaultPipelineAssetUsesManagedSelectionWhenPresent) {
Pipelines::ClearManagedRenderPipelineAssetDescriptor();
}
TEST(CameraRenderer_Test, DefaultPipelineAssetRefreshesWhenManagedSelectionChanges) {
Pipelines::ClearManagedRenderPipelineAssetDescriptor();
CameraRenderer renderer;
EXPECT_EQ(
dynamic_cast<const Pipelines::ManagedScriptableRenderPipelineAsset*>(
renderer.GetPipelineAsset()),
nullptr);
const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
"GameScripts",
"Gameplay",
"ManagedRenderPipelineProbeAsset"
};
Pipelines::SetManagedRenderPipelineAssetDescriptor(descriptor);
auto* managedAsset =
dynamic_cast<const Pipelines::ManagedScriptableRenderPipelineAsset*>(
renderer.GetPipelineAsset());
ASSERT_NE(managedAsset, nullptr);
EXPECT_EQ(managedAsset->GetDescriptor().assemblyName, "GameScripts");
EXPECT_EQ(managedAsset->GetDescriptor().namespaceName, "Gameplay");
EXPECT_EQ(managedAsset->GetDescriptor().className, "ManagedRenderPipelineProbeAsset");
Pipelines::ClearManagedRenderPipelineAssetDescriptor();
EXPECT_EQ(
dynamic_cast<const Pipelines::ManagedScriptableRenderPipelineAsset*>(
renderer.GetPipelineAsset()),
nullptr);
}
TEST(CameraRenderer_Test, RebindsToResolvedDefaultAssetWhenPreferredAssetCannotCreatePipeline) {
const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
"GameScripts",
@@ -4941,6 +4972,40 @@ TEST(CameraRenderer_Test, RebindsToResolvedDefaultAssetWhenPreferredAssetCannotC
Pipelines::ClearManagedRenderPipelineAssetDescriptor();
}
TEST(CameraRenderer_Test, RecreatesManagedPipelineWhenBridgeChanges) {
Pipelines::ClearManagedRenderPipelineBridge();
const Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
"GameScripts",
"Gameplay",
"ManagedRenderPipelineProbeAsset"
};
auto firstBridgeState = std::make_shared<MockManagedRenderPipelineBridgeState>();
Pipelines::SetManagedRenderPipelineBridge(
std::make_shared<MockManagedRenderPipelineBridge>(firstBridgeState));
CameraRenderer renderer(
std::make_shared<Pipelines::ManagedScriptableRenderPipelineAsset>(
descriptor));
ASSERT_NE(
dynamic_cast<Pipelines::ScriptableRenderPipelineHost*>(renderer.GetPipeline()),
nullptr);
EXPECT_EQ(firstBridgeState->createAssetRuntimeCalls, 1);
auto secondBridgeState = std::make_shared<MockManagedRenderPipelineBridgeState>();
Pipelines::SetManagedRenderPipelineBridge(
std::make_shared<MockManagedRenderPipelineBridge>(secondBridgeState));
auto* host =
dynamic_cast<Pipelines::ScriptableRenderPipelineHost*>(renderer.GetPipeline());
ASSERT_NE(host, nullptr);
EXPECT_NE(host->GetStageRecorder(), nullptr);
EXPECT_EQ(secondBridgeState->createAssetRuntimeCalls, 1);
Pipelines::ClearManagedRenderPipelineBridge();
}
TEST(SceneRenderer_Test, CreatesPipelineInstancesFromPipelineAssetsAndShutsDownReplacedPipelines) {
Scene scene("SceneRendererAssetScene");