Add render-graph main-scene pipeline recording
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include <XCEngine/RHI/RHITexture.h>
|
||||
#include <XCEngine/Core/Math/Vector4.h>
|
||||
#include <XCEngine/Rendering/Execution/CameraRenderer.h>
|
||||
#include <XCEngine/Rendering/Graph/RenderGraph.h>
|
||||
#include <XCEngine/Rendering/RenderPipelineAsset.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
#include <XCEngine/Rendering/Execution/SceneRenderer.h>
|
||||
@@ -28,7 +29,11 @@ struct MockPipelineState {
|
||||
int initializeCalls = 0;
|
||||
int shutdownCalls = 0;
|
||||
int renderCalls = 0;
|
||||
int recordMainSceneCalls = 0;
|
||||
int executeRecordedMainSceneCalls = 0;
|
||||
bool renderResult = true;
|
||||
bool supportsMainSceneRenderGraph = false;
|
||||
bool recordMainSceneResult = true;
|
||||
bool lastSurfaceAutoTransitionEnabled = true;
|
||||
uint32_t lastSurfaceWidth = 0;
|
||||
uint32_t lastSurfaceHeight = 0;
|
||||
@@ -51,6 +56,7 @@ struct MockPipelineState {
|
||||
RenderEnvironmentMode lastEnvironmentMode = RenderEnvironmentMode::None;
|
||||
bool lastHasSkybox = false;
|
||||
const XCEngine::Resources::Material* lastSkyboxMaterial = nullptr;
|
||||
bool lastRecordedMainSceneShadowHandleValid = false;
|
||||
std::vector<CameraComponent*> renderedCameras;
|
||||
std::vector<RenderClearFlags> renderedClearFlags;
|
||||
std::vector<XCEngine::Math::Color> renderedClearColors;
|
||||
@@ -399,6 +405,107 @@ public:
|
||||
++m_state->shutdownCalls;
|
||||
}
|
||||
|
||||
bool SupportsMainSceneRenderGraph() const override {
|
||||
return m_state->supportsMainSceneRenderGraph;
|
||||
}
|
||||
|
||||
bool RecordMainSceneRenderGraph(
|
||||
const RenderPipelineMainSceneRenderGraphContext& context) override {
|
||||
++m_state->recordMainSceneCalls;
|
||||
m_state->lastRecordedMainSceneShadowHandleValid =
|
||||
context.mainDirectionalShadowTexture.IsValid();
|
||||
if (!m_state->recordMainSceneResult) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const RenderSceneData* const sceneData = &context.sceneData;
|
||||
RenderSurface surface = context.surfaceTemplate;
|
||||
surface.SetAutoTransitionEnabled(false);
|
||||
const std::vector<RenderGraphTextureHandle> colorTargets = context.colorTargets;
|
||||
const RenderGraphTextureHandle depthTarget = context.depthTarget;
|
||||
const RenderGraphTextureHandle mainDirectionalShadowTexture =
|
||||
context.mainDirectionalShadowTexture;
|
||||
bool* const executionSucceeded = context.executionSucceeded;
|
||||
const std::shared_ptr<MockPipelineState> state = m_state;
|
||||
|
||||
context.graphBuilder.AddRasterPass(
|
||||
context.passName,
|
||||
[state,
|
||||
surface,
|
||||
sceneData,
|
||||
colorTargets,
|
||||
depthTarget,
|
||||
mainDirectionalShadowTexture,
|
||||
executionSucceeded](
|
||||
RenderGraphPassBuilder& passBuilder) {
|
||||
for (RenderGraphTextureHandle colorTarget : colorTargets) {
|
||||
if (colorTarget.IsValid()) {
|
||||
passBuilder.WriteTexture(colorTarget);
|
||||
}
|
||||
}
|
||||
if (depthTarget.IsValid()) {
|
||||
passBuilder.WriteDepthTexture(depthTarget);
|
||||
}
|
||||
if (mainDirectionalShadowTexture.IsValid()) {
|
||||
passBuilder.ReadTexture(mainDirectionalShadowTexture);
|
||||
}
|
||||
|
||||
passBuilder.SetExecuteCallback(
|
||||
[state,
|
||||
surface,
|
||||
sceneData,
|
||||
executionSucceeded](
|
||||
const RenderGraphExecutionContext&) {
|
||||
if (executionSucceeded != nullptr && !(*executionSucceeded)) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->eventLog.push_back("pipelineGraph");
|
||||
++state->executeRecordedMainSceneCalls;
|
||||
state->lastSurfaceAutoTransitionEnabled =
|
||||
surface.IsAutoTransitionEnabled();
|
||||
state->lastSurfaceWidth = surface.GetWidth();
|
||||
state->lastSurfaceHeight = surface.GetHeight();
|
||||
const XCEngine::Math::RectInt renderArea = surface.GetRenderArea();
|
||||
state->lastRenderAreaX = renderArea.x;
|
||||
state->lastRenderAreaY = renderArea.y;
|
||||
state->lastRenderAreaWidth = renderArea.width;
|
||||
state->lastRenderAreaHeight = renderArea.height;
|
||||
state->lastCamera = sceneData->camera;
|
||||
state->lastCameraViewportWidth = sceneData->cameraData.viewportWidth;
|
||||
state->lastCameraViewportHeight = sceneData->cameraData.viewportHeight;
|
||||
state->lastVisibleItemCount = sceneData->visibleItems.size();
|
||||
state->lastClearFlags = sceneData->cameraData.clearFlags;
|
||||
state->lastClearColor = sceneData->cameraData.clearColor;
|
||||
state->lastHasMainDirectionalShadow =
|
||||
sceneData->lighting.HasMainDirectionalShadow();
|
||||
state->lastShadowMap = sceneData->lighting.mainDirectionalShadow.shadowMap;
|
||||
state->lastShadowViewProjection =
|
||||
sceneData->lighting.mainDirectionalShadow.viewProjection;
|
||||
state->lastShadowMapMetrics =
|
||||
sceneData->lighting.mainDirectionalShadow.mapMetrics;
|
||||
state->lastShadowSampling =
|
||||
sceneData->lighting.mainDirectionalShadow.sampling;
|
||||
state->lastHasMainDirectionalShadowKeyword =
|
||||
XCEngine::Resources::ShaderKeywordSetContains(
|
||||
sceneData->globalShaderKeywords,
|
||||
"XC_MAIN_LIGHT_SHADOWS");
|
||||
state->lastEnvironmentMode = sceneData->environment.mode;
|
||||
state->lastHasSkybox = sceneData->environment.HasSkybox();
|
||||
state->lastSkyboxMaterial =
|
||||
sceneData->environment.materialSkybox.material;
|
||||
state->renderedCameras.push_back(sceneData->camera);
|
||||
state->renderedClearFlags.push_back(sceneData->cameraData.clearFlags);
|
||||
state->renderedClearColors.push_back(sceneData->cameraData.clearColor);
|
||||
if (executionSucceeded != nullptr) {
|
||||
*executionSucceeded = state->renderResult;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Render(
|
||||
const RenderContext&,
|
||||
const RenderSurface& surface,
|
||||
@@ -1452,6 +1559,37 @@ TEST(CameraRenderer_Test, UsesGraphManagedSurfaceForMainSceneWhenOutputAttachmen
|
||||
EXPECT_EQ(state->lastSurfaceHeight, 180u);
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, UsesPipelineRecordedMainSceneWhenSupported) {
|
||||
Scene scene("CameraRendererPipelineRecordedMainScene");
|
||||
|
||||
GameObject* cameraObject = scene.CreateGameObject("Camera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
camera->SetPrimary(true);
|
||||
camera->SetDepth(3.0f);
|
||||
|
||||
auto state = std::make_shared<MockPipelineState>();
|
||||
state->supportsMainSceneRenderGraph = true;
|
||||
CameraRenderer renderer(std::make_unique<MockPipeline>(state));
|
||||
|
||||
CameraRenderRequest request;
|
||||
request.scene = &scene;
|
||||
request.camera = camera;
|
||||
request.context = CreateValidContext();
|
||||
request.surface = RenderSurface(320, 180);
|
||||
request.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1));
|
||||
request.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
|
||||
request.cameraDepth = camera->GetDepth();
|
||||
|
||||
ASSERT_TRUE(renderer.Render(CameraFramePlan::FromRequest(request)));
|
||||
EXPECT_EQ(state->recordMainSceneCalls, 1);
|
||||
EXPECT_EQ(state->executeRecordedMainSceneCalls, 1);
|
||||
EXPECT_EQ(state->renderCalls, 0);
|
||||
EXPECT_EQ(state->eventLog, (std::vector<std::string>{ "pipelineGraph" }));
|
||||
EXPECT_FALSE(state->lastSurfaceAutoTransitionEnabled);
|
||||
EXPECT_EQ(state->lastSurfaceWidth, 320u);
|
||||
EXPECT_EQ(state->lastSurfaceHeight, 180u);
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) {
|
||||
Scene scene("CameraRendererAutoDirectionalShadowScene");
|
||||
|
||||
@@ -1540,6 +1678,59 @@ TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) {
|
||||
EXPECT_EQ(allocationState->destroyTextureCalls, 1);
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, PassesShadowDependencyToPipelineRecordedMainScene) {
|
||||
Scene scene("CameraRendererPipelineRecordedShadowDependency");
|
||||
|
||||
GameObject* cameraObject = scene.CreateGameObject("Camera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
camera->SetPrimary(true);
|
||||
camera->SetDepth(2.0f);
|
||||
|
||||
auto pipelineState = std::make_shared<MockPipelineState>();
|
||||
pipelineState->supportsMainSceneRenderGraph = true;
|
||||
auto allocationState = std::make_shared<MockShadowAllocationState>();
|
||||
MockShadowDevice device(allocationState);
|
||||
|
||||
RenderContext context = CreateValidContext();
|
||||
context.device = &device;
|
||||
|
||||
CameraRenderer renderer(
|
||||
std::make_unique<MockPipeline>(pipelineState),
|
||||
std::make_unique<MockObjectIdPass>(pipelineState));
|
||||
|
||||
auto shadowPass = std::make_unique<MockScenePass>(pipelineState, "shadowCaster");
|
||||
renderer.SetShadowCasterPass(std::move(shadowPass));
|
||||
|
||||
CameraRenderRequest request;
|
||||
request.scene = &scene;
|
||||
request.camera = camera;
|
||||
request.context = context;
|
||||
request.surface = RenderSurface(320, 180);
|
||||
request.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1));
|
||||
request.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
|
||||
request.cameraDepth = camera->GetDepth();
|
||||
request.directionalShadow.enabled = true;
|
||||
request.directionalShadow.mapWidth = 256;
|
||||
request.directionalShadow.mapHeight = 128;
|
||||
request.directionalShadow.cameraData.viewportWidth = 256;
|
||||
request.directionalShadow.cameraData.viewportHeight = 128;
|
||||
request.directionalShadow.cameraData.clearFlags = RenderClearFlags::Depth;
|
||||
request.directionalShadow.cameraData.worldPosition = XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f);
|
||||
request.directionalShadow.cameraData.viewProjection =
|
||||
XCEngine::Math::Matrix4x4::Translation(XCEngine::Math::Vector3(11.0f, 12.0f, 13.0f));
|
||||
|
||||
ASSERT_TRUE(renderer.Render(CameraFramePlan::FromRequest(request)));
|
||||
EXPECT_EQ(pipelineState->recordMainSceneCalls, 1);
|
||||
EXPECT_EQ(pipelineState->executeRecordedMainSceneCalls, 1);
|
||||
EXPECT_TRUE(pipelineState->lastRecordedMainSceneShadowHandleValid);
|
||||
EXPECT_EQ(
|
||||
pipelineState->eventLog,
|
||||
(std::vector<std::string>{
|
||||
"init:shadowCaster",
|
||||
"shadowCaster",
|
||||
"pipelineGraph" }));
|
||||
}
|
||||
|
||||
TEST(CameraRenderer_Test, ReusesDirectionalShadowSurfaceWhenPlanMatches) {
|
||||
Scene scene("CameraRendererDirectionalShadowReuseScene");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user