Introduce native render pipeline host

This commit is contained in:
2026-04-15 01:26:25 +08:00
parent d0ce2d7883
commit 82b8bd22cc
6 changed files with 272 additions and 62 deletions

View File

@@ -8,6 +8,7 @@
#include <XCEngine/RHI/RHITexture.h>
#include <XCEngine/Core/Math/Vector4.h>
#include <XCEngine/Rendering/Execution/CameraRenderer.h>
#include <XCEngine/Rendering/Execution/RenderPipelineHost.h>
#include <XCEngine/Rendering/Graph/RenderGraph.h>
#include <XCEngine/Rendering/RenderPipelineAsset.h>
#include <XCEngine/Rendering/RenderSurface.h>
@@ -3468,6 +3469,139 @@ TEST(SceneRenderer_Test, ForwardsPipelineLifetimeAndRenderCallsToCameraRenderer)
EXPECT_EQ(replacementState->shutdownCalls, 1);
}
TEST(RenderPipelineHost_Test, BuildsFramePlansFromRequestsUsingPipelineAssetDefaults) {
Scene scene("RenderPipelineHostBuildPlans");
GameObject* cameraObject = scene.CreateGameObject("Camera");
auto* camera = cameraObject->AddComponent<CameraComponent>();
camera->SetPrimary(true);
camera->SetDepth(2.0f);
FinalColorOverrideSettings cameraOverrides = {};
cameraOverrides.overrideExposureValue = true;
cameraOverrides.exposureValue = 1.8f;
camera->SetFinalColorOverrides(cameraOverrides);
auto assetState = std::make_shared<MockPipelineAssetState>();
assetState->defaultFinalColorSettings.outputTransferMode =
FinalColorOutputTransferMode::LinearToSRGB;
assetState->defaultFinalColorSettings.exposureMode =
FinalColorExposureMode::Fixed;
assetState->defaultFinalColorSettings.exposureValue = 1.25f;
CameraRenderRequest request = {};
request.scene = &scene;
request.camera = camera;
request.context = CreateValidContext();
request.surface = RenderSurface(640, 360);
request.cameraDepth = camera->GetDepth();
request.clearFlags = RenderClearFlags::All;
RenderPipelineHost host(std::make_shared<MockPipelineAsset>(assetState));
const std::vector<CameraFramePlan> plans =
host.BuildFramePlans({ request });
ASSERT_EQ(plans.size(), 1u);
const CameraFramePlan& plan = plans[0];
EXPECT_TRUE(plan.finalColorPolicy.hasPipelineDefaults);
EXPECT_TRUE(plan.finalColorPolicy.hasCameraOverrides);
EXPECT_EQ(
plan.finalColorPolicy.outputTransferMode,
FinalColorOutputTransferMode::LinearToSRGB);
EXPECT_EQ(
plan.finalColorPolicy.exposureMode,
FinalColorExposureMode::Fixed);
EXPECT_FLOAT_EQ(plan.finalColorPolicy.exposureValue, 1.8f);
}
TEST(RenderPipelineHost_Test, ForwardsPipelineLifetimeAndRenderCallsToCameraRenderer) {
Scene scene("RenderPipelineHostScene");
GameObject* cameraObject = scene.CreateGameObject("Camera");
auto* camera = cameraObject->AddComponent<CameraComponent>();
camera->SetPrimary(true);
camera->SetDepth(2.0f);
auto initialState = std::make_shared<MockPipelineState>();
auto replacementState = std::make_shared<MockPipelineState>();
{
auto initialPipeline = std::make_unique<MockPipeline>(initialState);
MockPipeline* initialPipelineRaw = initialPipeline.get();
RenderPipelineHost host(std::move(initialPipeline));
EXPECT_EQ(host.GetPipeline(), initialPipelineRaw);
auto replacementPipeline = std::make_unique<MockPipeline>(replacementState);
MockPipeline* replacementPipelineRaw = replacementPipeline.get();
host.SetPipeline(std::move(replacementPipeline));
EXPECT_EQ(initialState->shutdownCalls, 1);
EXPECT_EQ(host.GetPipeline(), replacementPipelineRaw);
CameraRenderRequest request = {};
request.scene = &scene;
request.camera = camera;
request.context = CreateValidContext();
request.surface = RenderSurface(800, 600);
request.cameraDepth = camera->GetDepth();
request.clearFlags = RenderClearFlags::All;
const std::vector<CameraFramePlan> plans =
host.BuildFramePlans({ request });
ASSERT_EQ(plans.size(), 1u);
ASSERT_TRUE(host.Render(plans));
EXPECT_EQ(replacementState->renderCalls, 1);
EXPECT_EQ(replacementState->lastSurfaceWidth, 800u);
EXPECT_EQ(replacementState->lastSurfaceHeight, 600u);
EXPECT_EQ(replacementState->lastCamera, camera);
}
EXPECT_EQ(initialState->shutdownCalls, 1);
EXPECT_EQ(replacementState->shutdownCalls, 1);
}
TEST(RenderPipelineHost_Test, SortsManualFramePlansByDepthBeforeRendering) {
Scene scene("RenderPipelineHostManualRequests");
GameObject* farCameraObject = scene.CreateGameObject("FarCamera");
auto* farCamera = farCameraObject->AddComponent<CameraComponent>();
farCamera->SetPrimary(true);
farCamera->SetDepth(10.0f);
GameObject* nearCameraObject = scene.CreateGameObject("NearCamera");
auto* nearCamera = nearCameraObject->AddComponent<CameraComponent>();
nearCamera->SetPrimary(false);
nearCamera->SetDepth(1.0f);
auto state = std::make_shared<MockPipelineState>();
RenderPipelineHost host(std::make_unique<MockPipeline>(state));
CameraRenderRequest farRequest;
farRequest.scene = &scene;
farRequest.camera = farCamera;
farRequest.context = CreateValidContext();
farRequest.surface = RenderSurface(800, 600);
farRequest.cameraDepth = farCamera->GetDepth();
farRequest.cameraStackOrder = 1;
farRequest.clearFlags = RenderClearFlags::None;
CameraRenderRequest nearRequest = farRequest;
nearRequest.camera = nearCamera;
nearRequest.cameraDepth = nearCamera->GetDepth();
nearRequest.cameraStackOrder = 0;
nearRequest.clearFlags = RenderClearFlags::Depth;
const std::vector<CameraFramePlan> plans =
host.BuildFramePlans({ farRequest, nearRequest });
ASSERT_TRUE(host.Render(plans));
ASSERT_EQ(state->renderedCameras.size(), 2u);
ASSERT_EQ(state->renderedClearFlags.size(), 2u);
EXPECT_EQ(state->renderedCameras[0], nearCamera);
EXPECT_EQ(state->renderedClearFlags[0], RenderClearFlags::Depth);
EXPECT_EQ(state->renderedCameras[1], farCamera);
EXPECT_EQ(state->renderedClearFlags[1], RenderClearFlags::None);
}
TEST(SceneRenderer_Test, CreatesPipelineInstancesFromPipelineAssetsAndShutsDownReplacedPipelines) {
Scene scene("SceneRendererAssetScene");