refactor: generalize renderer builtin post process

This commit is contained in:
2026-04-02 14:49:00 +08:00
parent 697deb4e41
commit ec7a15d85b
17 changed files with 430 additions and 327 deletions

View File

@@ -114,21 +114,21 @@ inline ViewportRenderFallbackPolicy BuildGameViewportRenderFailurePolicy(
inline void ApplySceneViewportRenderRequestSetup(
const ViewportRenderTargets& targets,
const Rendering::BuiltinSceneViewPostProcessRequest* builtinSceneViewPostProcess,
const Rendering::BuiltinPostProcessRequest* builtinPostProcess,
Rendering::RenderPassSequence* postPasses,
Rendering::CameraRenderRequest& request) {
request.postScenePasses = nullptr;
request.objectId = {};
request.builtinSceneViewPostProcess = {};
request.builtinPostProcess = {};
if (postPasses != nullptr && postPasses->GetPassCount() > 0) {
request.postScenePasses = postPasses;
}
if (builtinSceneViewPostProcess != nullptr &&
builtinSceneViewPostProcess->IsRequested()) {
request.builtinSceneViewPostProcess = *builtinSceneViewPostProcess;
request.builtinSceneViewPostProcess.objectIdTextureView = targets.objectIdShaderView;
if (builtinPostProcess != nullptr &&
builtinPostProcess->IsRequested()) {
request.builtinPostProcess = *builtinPostProcess;
request.builtinPostProcess.objectIdTextureView = targets.objectIdShaderView;
}
if (targets.objectIdView == nullptr) {

View File

@@ -278,7 +278,7 @@ private:
struct SceneViewportRenderState {
SceneViewportOverlayData overlay = {};
Rendering::BuiltinSceneViewPostProcessRequest builtinSceneViewPostProcess = {};
Rendering::BuiltinPostProcessRequest builtinPostProcess = {};
std::vector<uint64_t> selectedObjectIds;
};
@@ -414,7 +414,7 @@ private:
ViewportEntry& entry,
const SceneViewportOverlayData& overlay,
const std::vector<uint64_t>& selectedObjectIds,
Rendering::BuiltinSceneViewPostProcessRequest& outRequest) {
Rendering::BuiltinPostProcessRequest& outRequest) {
if (!overlay.valid) {
outRequest = {};
return;
@@ -450,7 +450,7 @@ private:
entry,
outState.overlay,
outState.selectedObjectIds,
outState.builtinSceneViewPostProcess);
outState.builtinPostProcess);
}
bool RenderSceneViewportEntry(
@@ -495,7 +495,7 @@ private:
ApplySceneViewportRenderRequestSetup(
entry.renderTargets,
&sceneState.builtinSceneViewPostProcess,
&sceneState.builtinPostProcess,
nullptr,
requests[0]);
requests[0].hasClearColorOverride = true;

View File

@@ -357,14 +357,14 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/SceneRenderer.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinSceneViewPostPassPlan.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinSceneViewPostPassSequenceBuilder.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinPostProcessPassPlan.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinPostProcessPassSequenceBuilder.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/CameraRenderer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinInfiniteGridPass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinSceneViewPostPassSequenceBuilder.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinPostProcessPassSequenceBuilder.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdOutlinePass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSurface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSceneExtractor.cpp

View File

@@ -32,14 +32,14 @@ struct ObjectIdRenderRequest {
}
};
struct BuiltinSceneViewPostProcessRequest {
struct BuiltinPostProcessRequest {
Passes::InfiniteGridPassData gridPassData = {};
RHI::RHIResourceView* objectIdTextureView = nullptr;
std::vector<uint64_t> selectedObjectIds = {};
Passes::ObjectIdOutlineStyle outlineStyle = {};
bool IsRequested() const {
return gridPassData.valid;
return gridPassData.valid || !selectedObjectIds.empty();
}
};
@@ -49,7 +49,7 @@ struct CameraRenderRequest {
RenderContext context;
RenderSurface surface;
ObjectIdRenderRequest objectId;
BuiltinSceneViewPostProcessRequest builtinSceneViewPostProcess;
BuiltinPostProcessRequest builtinPostProcess;
float cameraDepth = 0.0f;
uint8_t cameraStackOrder = 0;
RenderClearFlags clearFlags = RenderClearFlags::All;

View File

@@ -2,7 +2,7 @@
#include <XCEngine/Rendering/CameraRenderRequest.h>
#include <XCEngine/Rendering/ObjectIdPass.h>
#include <XCEngine/Rendering/Passes/BuiltinSceneViewPostPassSequenceBuilder.h>
#include <XCEngine/Rendering/Passes/BuiltinPostProcessPassSequenceBuilder.h>
#include <XCEngine/Rendering/RenderPipeline.h>
#include <memory>
@@ -44,7 +44,7 @@ private:
std::shared_ptr<const RenderPipelineAsset> m_pipelineAsset;
std::unique_ptr<RenderPipeline> m_pipeline;
std::unique_ptr<ObjectIdPass> m_objectIdPass;
Passes::BuiltinSceneViewPostPassSequenceBuilder m_builtinSceneViewPostProcessBuilder;
Passes::BuiltinPostProcessPassSequenceBuilder m_builtinPostProcessBuilder;
};
} // namespace Rendering

View File

@@ -0,0 +1,91 @@
#pragma once
#include <cstdint>
#include <vector>
namespace XCEngine {
namespace Rendering {
namespace Passes {
enum class BuiltinPostProcessPassStep : uint8_t {
ColorToRenderTarget,
InfiniteGrid,
SelectionOutline,
ColorToShaderResource,
SelectionMaskDebug
};
struct BuiltinPostProcessPassPlanInput {
bool hasGridOverlay = false;
bool hasSelection = false;
bool debugSelectionMask = false;
bool hasObjectIdShaderView = false;
};
struct BuiltinPostProcessPassPlan {
bool valid = false;
std::vector<BuiltinPostProcessPassStep> steps;
};
inline BuiltinPostProcessPassPlan BuildBuiltinPostProcessPassPlan(
const BuiltinPostProcessPassPlanInput& input) {
BuiltinPostProcessPassPlan plan = {};
plan.valid = true;
if (input.debugSelectionMask) {
if (input.hasSelection && input.hasObjectIdShaderView) {
plan.steps = {
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::SelectionMaskDebug,
BuiltinPostProcessPassStep::ColorToShaderResource
};
return plan;
}
if (input.hasGridOverlay) {
plan.steps = {
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::ColorToShaderResource
};
return plan;
}
plan.valid = false;
return plan;
}
if (input.hasGridOverlay && input.hasSelection && input.hasObjectIdShaderView) {
plan.steps = {
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::InfiniteGrid,
BuiltinPostProcessPassStep::SelectionOutline,
BuiltinPostProcessPassStep::ColorToShaderResource
};
return plan;
}
if (input.hasGridOverlay) {
plan.steps = {
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::InfiniteGrid,
BuiltinPostProcessPassStep::ColorToShaderResource
};
return plan;
}
if (input.hasSelection && input.hasObjectIdShaderView) {
plan.steps = {
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::SelectionOutline,
BuiltinPostProcessPassStep::ColorToShaderResource
};
return plan;
}
plan.valid = false;
return plan;
}
} // namespace Passes
} // namespace Rendering
} // namespace XCEngine

View File

@@ -11,26 +11,26 @@ namespace XCEngine {
namespace Rendering {
namespace Passes {
struct BuiltinSceneViewPostPassSequenceInput {
struct BuiltinPostProcessPassSequenceInput {
InfiniteGridPassData gridPassData = {};
RHI::RHIResourceView* objectIdTextureView = nullptr;
std::vector<uint64_t> selectedObjectIds = {};
ObjectIdOutlineStyle outlineStyle = {};
};
struct BuiltinSceneViewPostPassSequenceBuildResult {
struct BuiltinPostProcessPassSequenceBuildResult {
bool valid = false;
bool missingObjectIdTextureViewForSelection = false;
};
class BuiltinSceneViewPostPassSequenceBuilder {
class BuiltinPostProcessPassSequenceBuilder {
public:
~BuiltinSceneViewPostPassSequenceBuilder();
~BuiltinPostProcessPassSequenceBuilder();
void Shutdown();
BuiltinSceneViewPostPassSequenceBuildResult Build(
const BuiltinSceneViewPostPassSequenceInput& input,
BuiltinPostProcessPassSequenceBuildResult Build(
const BuiltinPostProcessPassSequenceInput& input,
RenderPassSequence& outSequence);
private:

View File

@@ -1,67 +0,0 @@
#pragma once
#include <cstdint>
#include <vector>
namespace XCEngine {
namespace Rendering {
namespace Passes {
enum class BuiltinSceneViewPostPassStep : uint8_t {
ColorToRenderTarget,
InfiniteGrid,
SelectionOutline,
ColorToShaderResource,
SelectionMaskDebug
};
struct BuiltinSceneViewPostPassPlanInput {
bool overlayValid = false;
bool hasSelection = false;
bool debugSelectionMask = false;
bool hasObjectIdShaderView = false;
};
struct BuiltinSceneViewPostPassPlan {
bool valid = false;
std::vector<BuiltinSceneViewPostPassStep> steps;
};
inline BuiltinSceneViewPostPassPlan BuildBuiltinSceneViewPostPassPlan(
const BuiltinSceneViewPostPassPlanInput& input) {
BuiltinSceneViewPostPassPlan plan = {};
if (!input.overlayValid) {
return plan;
}
plan.valid = true;
if (input.debugSelectionMask) {
plan.steps.push_back(BuiltinSceneViewPostPassStep::ColorToRenderTarget);
if (input.hasSelection && input.hasObjectIdShaderView) {
plan.steps.push_back(BuiltinSceneViewPostPassStep::SelectionMaskDebug);
}
plan.steps.push_back(BuiltinSceneViewPostPassStep::ColorToShaderResource);
return plan;
}
plan.steps.push_back(BuiltinSceneViewPostPassStep::ColorToRenderTarget);
plan.steps.push_back(BuiltinSceneViewPostPassStep::InfiniteGrid);
if (input.hasSelection && input.hasObjectIdShaderView) {
plan.steps = {
BuiltinSceneViewPostPassStep::ColorToRenderTarget,
BuiltinSceneViewPostPassStep::InfiniteGrid,
BuiltinSceneViewPostPassStep::SelectionOutline,
BuiltinSceneViewPostPassStep::ColorToShaderResource
};
return plan;
}
plan.steps.push_back(BuiltinSceneViewPostPassStep::ColorToShaderResource);
return plan;
}
} // namespace Passes
} // namespace Rendering
} // namespace XCEngine

View File

@@ -86,7 +86,7 @@ CameraRenderer::~CameraRenderer() {
if (m_objectIdPass != nullptr) {
m_objectIdPass->Shutdown();
}
m_builtinSceneViewPostProcessBuilder.Shutdown();
m_builtinPostProcessBuilder.Shutdown();
}
void CameraRenderer::SetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
@@ -136,8 +136,8 @@ bool CameraRenderer::Render(
!request.objectId.IsValid()) {
return false;
}
if (request.builtinSceneViewPostProcess.IsRequested() &&
request.builtinSceneViewPostProcess.objectIdTextureView != nullptr &&
if (request.builtinPostProcess.IsRequested() &&
request.builtinPostProcess.objectIdTextureView != nullptr &&
!request.objectId.IsRequested()) {
return false;
}
@@ -201,36 +201,36 @@ bool CameraRenderer::Render(
return false;
}
RenderPassSequence builtinSceneViewPostPasses = {};
bool builtinSceneViewPostPassesInitialized = false;
if (request.builtinSceneViewPostProcess.IsRequested()) {
const Passes::BuiltinSceneViewPostPassSequenceBuildResult buildResult =
m_builtinSceneViewPostProcessBuilder.Build(
RenderPassSequence builtinPostProcessPasses = {};
bool builtinPostProcessPassesInitialized = false;
if (request.builtinPostProcess.IsRequested()) {
const Passes::BuiltinPostProcessPassSequenceBuildResult buildResult =
m_builtinPostProcessBuilder.Build(
{
request.builtinSceneViewPostProcess.gridPassData,
request.builtinSceneViewPostProcess.objectIdTextureView,
request.builtinSceneViewPostProcess.selectedObjectIds,
request.builtinSceneViewPostProcess.outlineStyle
request.builtinPostProcess.gridPassData,
request.builtinPostProcess.objectIdTextureView,
request.builtinPostProcess.selectedObjectIds,
request.builtinPostProcess.outlineStyle
},
builtinSceneViewPostPasses);
builtinPostProcessPasses);
if (!buildResult.valid ||
!InitializePassSequence(
&builtinSceneViewPostPasses,
&builtinPostProcessPasses,
request.context,
builtinSceneViewPostPassesInitialized)) {
builtinPostProcessPassesInitialized)) {
ShutdownPassSequence(request.postScenePasses, postScenePassesInitialized);
ShutdownPassSequence(request.preScenePasses, preScenePassesInitialized);
return false;
}
if (!builtinSceneViewPostPasses.Execute(passContext)) {
ShutdownPassSequence(&builtinSceneViewPostPasses, builtinSceneViewPostPassesInitialized);
if (!builtinPostProcessPasses.Execute(passContext)) {
ShutdownPassSequence(&builtinPostProcessPasses, builtinPostProcessPassesInitialized);
ShutdownPassSequence(request.postScenePasses, postScenePassesInitialized);
ShutdownPassSequence(request.preScenePasses, preScenePassesInitialized);
return false;
}
}
ShutdownPassSequence(&builtinSceneViewPostPasses, builtinSceneViewPostPassesInitialized);
ShutdownPassSequence(&builtinPostProcessPasses, builtinPostProcessPassesInitialized);
ShutdownPassSequence(request.postScenePasses, postScenePassesInitialized);
ShutdownPassSequence(request.preScenePasses, preScenePassesInitialized);

View File

@@ -1,6 +1,6 @@
#include "Rendering/Passes/BuiltinSceneViewPostPassSequenceBuilder.h"
#include "Rendering/Passes/BuiltinPostProcessPassSequenceBuilder.h"
#include "Rendering/Passes/BuiltinSceneViewPostPassPlan.h"
#include "Rendering/Passes/BuiltinPostProcessPassPlan.h"
#include <XCEngine/Core/Math/Color.h>
#include <XCEngine/RHI/RHICommandList.h>
@@ -47,19 +47,19 @@ std::unique_ptr<RenderPass> MakeLambdaRenderPass(const char* name, Callback&& ca
} // namespace
BuiltinSceneViewPostPassSequenceBuilder::~BuiltinSceneViewPostPassSequenceBuilder() {
BuiltinPostProcessPassSequenceBuilder::~BuiltinPostProcessPassSequenceBuilder() {
Shutdown();
}
void BuiltinSceneViewPostPassSequenceBuilder::Shutdown() {
void BuiltinPostProcessPassSequenceBuilder::Shutdown() {
m_outlinePass.Shutdown();
m_gridPass.Shutdown();
}
BuiltinSceneViewPostPassSequenceBuildResult BuiltinSceneViewPostPassSequenceBuilder::Build(
const BuiltinSceneViewPostPassSequenceInput& input,
BuiltinPostProcessPassSequenceBuildResult BuiltinPostProcessPassSequenceBuilder::Build(
const BuiltinPostProcessPassSequenceInput& input,
RenderPassSequence& outSequence) {
BuiltinSceneViewPostPassSequenceBuildResult result = {};
BuiltinPostProcessPassSequenceBuildResult result = {};
const bool hasSelection = !input.selectedObjectIds.empty();
const bool hasObjectIdTextureView = input.objectIdTextureView != nullptr;
@@ -67,7 +67,7 @@ BuiltinSceneViewPostPassSequenceBuildResult BuiltinSceneViewPostPassSequenceBuil
result.missingObjectIdTextureViewForSelection = true;
}
const BuiltinSceneViewPostPassPlan plan = BuildBuiltinSceneViewPostPassPlan({
const BuiltinPostProcessPassPlan plan = BuildBuiltinPostProcessPassPlan({
input.gridPassData.valid,
hasSelection,
input.outlineStyle.debugSelectionMask,
@@ -78,25 +78,25 @@ BuiltinSceneViewPostPassSequenceBuildResult BuiltinSceneViewPostPassSequenceBuil
}
result.valid = true;
for (const BuiltinSceneViewPostPassStep step : plan.steps) {
for (const BuiltinPostProcessPassStep step : plan.steps) {
switch (step) {
case BuiltinSceneViewPostPassStep::ColorToRenderTarget:
case BuiltinPostProcessPassStep::ColorToRenderTarget:
AddColorToRenderTargetPass(outSequence);
break;
case BuiltinSceneViewPostPassStep::InfiniteGrid:
case BuiltinPostProcessPassStep::InfiniteGrid:
AddInfiniteGridPass(input.gridPassData, outSequence);
break;
case BuiltinSceneViewPostPassStep::SelectionOutline:
case BuiltinPostProcessPassStep::SelectionOutline:
AddSelectionOutlinePass(
input.objectIdTextureView,
input.selectedObjectIds,
input.outlineStyle,
outSequence);
break;
case BuiltinSceneViewPostPassStep::ColorToShaderResource:
case BuiltinPostProcessPassStep::ColorToShaderResource:
AddColorToShaderResourcePass(outSequence);
break;
case BuiltinSceneViewPostPassStep::SelectionMaskDebug:
case BuiltinPostProcessPassStep::SelectionMaskDebug:
AddSelectionMaskDebugPass(
input.objectIdTextureView,
input.selectedObjectIds,
@@ -111,7 +111,7 @@ BuiltinSceneViewPostPassSequenceBuildResult BuiltinSceneViewPostPassSequenceBuil
return result;
}
void BuiltinSceneViewPostPassSequenceBuilder::AddColorToRenderTargetPass(
void BuiltinPostProcessPassSequenceBuilder::AddColorToRenderTargetPass(
RenderPassSequence& outSequence) {
outSequence.AddPass(MakeLambdaRenderPass(
"SceneColorToRenderTarget",
@@ -129,7 +129,7 @@ void BuiltinSceneViewPostPassSequenceBuilder::AddColorToRenderTargetPass(
}));
}
void BuiltinSceneViewPostPassSequenceBuilder::AddInfiniteGridPass(
void BuiltinPostProcessPassSequenceBuilder::AddInfiniteGridPass(
const InfiniteGridPassData& gridPassData,
RenderPassSequence& outSequence) {
outSequence.AddPass(MakeLambdaRenderPass(
@@ -142,7 +142,7 @@ void BuiltinSceneViewPostPassSequenceBuilder::AddInfiniteGridPass(
}));
}
void BuiltinSceneViewPostPassSequenceBuilder::AddSelectionOutlinePass(
void BuiltinPostProcessPassSequenceBuilder::AddSelectionOutlinePass(
RHI::RHIResourceView* objectIdTextureView,
const std::vector<uint64_t>& selectedObjectIds,
const ObjectIdOutlineStyle& outlineStyle,
@@ -159,7 +159,7 @@ void BuiltinSceneViewPostPassSequenceBuilder::AddSelectionOutlinePass(
}));
}
void BuiltinSceneViewPostPassSequenceBuilder::AddColorToShaderResourcePass(
void BuiltinPostProcessPassSequenceBuilder::AddColorToShaderResourcePass(
RenderPassSequence& outSequence) {
outSequence.AddPass(MakeLambdaRenderPass(
"SceneColorToShaderResource",
@@ -177,7 +177,7 @@ void BuiltinSceneViewPostPassSequenceBuilder::AddColorToShaderResourcePass(
}));
}
void BuiltinSceneViewPostPassSequenceBuilder::AddSelectionMaskDebugPass(
void BuiltinPostProcessPassSequenceBuilder::AddSelectionMaskDebugPass(
RHI::RHIResourceView* objectIdTextureView,
const std::vector<uint64_t>& selectedObjectIds,
const ObjectIdOutlineStyle& outlineStyle,

View File

@@ -5,8 +5,8 @@ project(XCEngine_RenderingUnitTests)
set(RENDERING_UNIT_TEST_SOURCES
test_render_pass.cpp
test_builtin_forward_pipeline.cpp
test_builtin_scene_view_post_pass_plan.cpp
test_builtin_scene_view_post_pass_sequence_builder.cpp
test_builtin_post_process_pass_plan.cpp
test_builtin_post_process_pass_sequence_builder.cpp
test_camera_scene_renderer.cpp
test_scene_render_request_planner.cpp
test_scene_render_request_utils.cpp

View File

@@ -0,0 +1,143 @@
#include <gtest/gtest.h>
#include <XCEngine/Rendering/Passes/BuiltinPostProcessPassPlan.h>
#include <vector>
namespace {
using XCEngine::Rendering::Passes::BuildBuiltinPostProcessPassPlan;
using XCEngine::Rendering::Passes::BuiltinPostProcessPassPlanInput;
using XCEngine::Rendering::Passes::BuiltinPostProcessPassStep;
TEST(BuiltinPostProcessPassPlan_Test, ReturnsInvalidPlanWhenNoBuiltinEffectIsAvailable) {
const auto plan = BuildBuiltinPostProcessPassPlan({});
EXPECT_FALSE(plan.valid);
EXPECT_TRUE(plan.steps.empty());
}
TEST(BuiltinPostProcessPassPlan_Test, BuildsGridOnlyPlanWhenNothingIsSelected) {
BuiltinPostProcessPassPlanInput input = {};
input.hasGridOverlay = true;
const auto plan = BuildBuiltinPostProcessPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinPostProcessPassStep>{
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::InfiniteGrid,
BuiltinPostProcessPassStep::ColorToShaderResource
}));
}
TEST(BuiltinPostProcessPassPlan_Test, BuildsGridAndSelectionOutlinePlanWhenObjectIdShaderViewExists) {
BuiltinPostProcessPassPlanInput input = {};
input.hasGridOverlay = true;
input.hasSelection = true;
input.hasObjectIdShaderView = true;
const auto plan = BuildBuiltinPostProcessPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinPostProcessPassStep>{
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::InfiniteGrid,
BuiltinPostProcessPassStep::SelectionOutline,
BuiltinPostProcessPassStep::ColorToShaderResource
}));
}
TEST(BuiltinPostProcessPassPlan_Test, SkipsSelectionOutlineWhenObjectIdShaderViewIsMissingAndGridExists) {
BuiltinPostProcessPassPlanInput input = {};
input.hasGridOverlay = true;
input.hasSelection = true;
const auto plan = BuildBuiltinPostProcessPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinPostProcessPassStep>{
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::InfiniteGrid,
BuiltinPostProcessPassStep::ColorToShaderResource
}));
}
TEST(BuiltinPostProcessPassPlan_Test, BuildsDebugMaskPlanFromObjectIdShaderView) {
BuiltinPostProcessPassPlanInput input = {};
input.hasGridOverlay = true;
input.hasSelection = true;
input.debugSelectionMask = true;
input.hasObjectIdShaderView = true;
const auto plan = BuildBuiltinPostProcessPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinPostProcessPassStep>{
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::SelectionMaskDebug,
BuiltinPostProcessPassStep::ColorToShaderResource
}));
}
TEST(BuiltinPostProcessPassPlan_Test, DebugMaskPlanFallsBackToTransitionsWhenGridExistsButObjectIdShaderViewIsMissing) {
BuiltinPostProcessPassPlanInput input = {};
input.hasGridOverlay = true;
input.hasSelection = true;
input.debugSelectionMask = true;
const auto plan = BuildBuiltinPostProcessPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinPostProcessPassStep>{
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::ColorToShaderResource
}));
}
TEST(BuiltinPostProcessPassPlan_Test, BuildsSelectionOnlyOutlinePlanWhenGridOverlayIsUnavailable) {
BuiltinPostProcessPassPlanInput input = {};
input.hasSelection = true;
input.hasObjectIdShaderView = true;
const auto plan = BuildBuiltinPostProcessPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinPostProcessPassStep>{
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::SelectionOutline,
BuiltinPostProcessPassStep::ColorToShaderResource
}));
}
TEST(BuiltinPostProcessPassPlan_Test, BuildsDebugMaskWithoutGridWhenObjectIdShaderViewExists) {
BuiltinPostProcessPassPlanInput input = {};
input.hasSelection = true;
input.debugSelectionMask = true;
input.hasObjectIdShaderView = true;
const auto plan = BuildBuiltinPostProcessPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinPostProcessPassStep>{
BuiltinPostProcessPassStep::ColorToRenderTarget,
BuiltinPostProcessPassStep::SelectionMaskDebug,
BuiltinPostProcessPassStep::ColorToShaderResource
}));
}
} // namespace

View File

@@ -0,0 +1,109 @@
#include <gtest/gtest.h>
#include <XCEngine/Rendering/Passes/BuiltinPostProcessPassSequenceBuilder.h>
namespace {
using XCEngine::Rendering::Passes::BuiltinPostProcessPassSequenceBuilder;
using XCEngine::Rendering::Passes::BuiltinPostProcessPassSequenceInput;
using XCEngine::Rendering::Passes::ObjectIdOutlineStyle;
using XCEngine::Rendering::RenderPassSequence;
TEST(BuiltinPostProcessPassSequenceBuilder_Test, ReturnsInvalidSequenceWhenNoBuiltinEffectIsRequested) {
BuiltinPostProcessPassSequenceBuilder builder;
RenderPassSequence sequence;
const auto result = builder.Build({}, sequence);
EXPECT_FALSE(result.valid);
EXPECT_FALSE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 0u);
}
TEST(BuiltinPostProcessPassSequenceBuilder_Test, BuildsGridOnlySequenceWhenNothingIsSelected) {
BuiltinPostProcessPassSequenceBuilder builder;
RenderPassSequence sequence;
BuiltinPostProcessPassSequenceInput input = {};
input.gridPassData.valid = true;
const auto result = builder.Build(input, sequence);
EXPECT_TRUE(result.valid);
EXPECT_FALSE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 3u);
}
TEST(BuiltinPostProcessPassSequenceBuilder_Test, BuildsGridAndSelectionOutlineSequenceWhenObjectIdTextureViewExists) {
BuiltinPostProcessPassSequenceBuilder builder;
RenderPassSequence sequence;
BuiltinPostProcessPassSequenceInput input = {};
input.gridPassData.valid = true;
input.objectIdTextureView = reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1);
input.selectedObjectIds = { 7u, 11u };
const auto result = builder.Build(input, sequence);
EXPECT_TRUE(result.valid);
EXPECT_FALSE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 4u);
}
TEST(BuiltinPostProcessPassSequenceBuilder_Test, ReportsMissingObjectIdTextureViewAndFallsBackToGridOnlySequence) {
BuiltinPostProcessPassSequenceBuilder builder;
RenderPassSequence sequence;
BuiltinPostProcessPassSequenceInput input = {};
input.gridPassData.valid = true;
input.selectedObjectIds = { 42u };
const auto result = builder.Build(input, sequence);
EXPECT_TRUE(result.valid);
EXPECT_TRUE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 3u);
}
TEST(BuiltinPostProcessPassSequenceBuilder_Test, BuildsDebugMaskSequenceWhenRequested) {
BuiltinPostProcessPassSequenceBuilder builder;
RenderPassSequence sequence;
BuiltinPostProcessPassSequenceInput input = {};
input.gridPassData.valid = true;
input.objectIdTextureView = reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1);
input.selectedObjectIds = { 5u };
input.outlineStyle = ObjectIdOutlineStyle{};
input.outlineStyle.debugSelectionMask = true;
const auto result = builder.Build(input, sequence);
EXPECT_TRUE(result.valid);
EXPECT_FALSE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 3u);
}
TEST(BuiltinPostProcessPassSequenceBuilder_Test, BuildsSelectionOnlyOutlineSequenceWhenGridDataIsUnavailable) {
BuiltinPostProcessPassSequenceBuilder builder;
RenderPassSequence sequence;
BuiltinPostProcessPassSequenceInput input = {};
input.objectIdTextureView = reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1);
input.selectedObjectIds = { 13u };
const auto result = builder.Build(input, sequence);
EXPECT_TRUE(result.valid);
EXPECT_FALSE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 3u);
}
TEST(BuiltinPostProcessPassSequenceBuilder_Test, ReportsInvalidSelectionOnlySequenceWhenObjectIdTextureViewIsMissing) {
BuiltinPostProcessPassSequenceBuilder builder;
RenderPassSequence sequence;
BuiltinPostProcessPassSequenceInput input = {};
input.selectedObjectIds = { 13u };
const auto result = builder.Build(input, sequence);
EXPECT_FALSE(result.valid);
EXPECT_TRUE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 0u);
}
} // namespace

View File

@@ -1,108 +0,0 @@
#include <gtest/gtest.h>
#include <XCEngine/Rendering/Passes/BuiltinSceneViewPostPassPlan.h>
#include <vector>
namespace {
using XCEngine::Rendering::Passes::BuildBuiltinSceneViewPostPassPlan;
using XCEngine::Rendering::Passes::BuiltinSceneViewPostPassPlanInput;
using XCEngine::Rendering::Passes::BuiltinSceneViewPostPassStep;
TEST(BuiltinSceneViewPostPassPlan_Test, ReturnsInvalidPlanWhenOverlayIsUnavailable) {
const auto plan = BuildBuiltinSceneViewPostPassPlan({});
EXPECT_FALSE(plan.valid);
EXPECT_TRUE(plan.steps.empty());
}
TEST(BuiltinSceneViewPostPassPlan_Test, BuildsGridOnlyPlanWhenNothingIsSelected) {
BuiltinSceneViewPostPassPlanInput input = {};
input.overlayValid = true;
const auto plan = BuildBuiltinSceneViewPostPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinSceneViewPostPassStep>{
BuiltinSceneViewPostPassStep::ColorToRenderTarget,
BuiltinSceneViewPostPassStep::InfiniteGrid,
BuiltinSceneViewPostPassStep::ColorToShaderResource
}));
}
TEST(BuiltinSceneViewPostPassPlan_Test, BuildsSelectionOutlinePlanWhenObjectIdShaderViewExists) {
BuiltinSceneViewPostPassPlanInput input = {};
input.overlayValid = true;
input.hasSelection = true;
input.hasObjectIdShaderView = true;
const auto plan = BuildBuiltinSceneViewPostPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinSceneViewPostPassStep>{
BuiltinSceneViewPostPassStep::ColorToRenderTarget,
BuiltinSceneViewPostPassStep::InfiniteGrid,
BuiltinSceneViewPostPassStep::SelectionOutline,
BuiltinSceneViewPostPassStep::ColorToShaderResource
}));
}
TEST(BuiltinSceneViewPostPassPlan_Test, SkipsSelectionOutlineWhenObjectIdShaderViewIsMissing) {
BuiltinSceneViewPostPassPlanInput input = {};
input.overlayValid = true;
input.hasSelection = true;
const auto plan = BuildBuiltinSceneViewPostPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinSceneViewPostPassStep>{
BuiltinSceneViewPostPassStep::ColorToRenderTarget,
BuiltinSceneViewPostPassStep::InfiniteGrid,
BuiltinSceneViewPostPassStep::ColorToShaderResource
}));
}
TEST(BuiltinSceneViewPostPassPlan_Test, BuildsDebugMaskPlanFromObjectIdShaderView) {
BuiltinSceneViewPostPassPlanInput input = {};
input.overlayValid = true;
input.hasSelection = true;
input.debugSelectionMask = true;
input.hasObjectIdShaderView = true;
const auto plan = BuildBuiltinSceneViewPostPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinSceneViewPostPassStep>{
BuiltinSceneViewPostPassStep::ColorToRenderTarget,
BuiltinSceneViewPostPassStep::SelectionMaskDebug,
BuiltinSceneViewPostPassStep::ColorToShaderResource
}));
}
TEST(BuiltinSceneViewPostPassPlan_Test, DebugMaskPlanFallsBackWhenObjectIdShaderViewIsMissing) {
BuiltinSceneViewPostPassPlanInput input = {};
input.overlayValid = true;
input.hasSelection = true;
input.debugSelectionMask = true;
const auto plan = BuildBuiltinSceneViewPostPassPlan(input);
ASSERT_TRUE(plan.valid);
EXPECT_EQ(
plan.steps,
(std::vector<BuiltinSceneViewPostPassStep>{
BuiltinSceneViewPostPassStep::ColorToRenderTarget,
BuiltinSceneViewPostPassStep::ColorToShaderResource
}));
}
} // namespace

View File

@@ -1,82 +0,0 @@
#include <gtest/gtest.h>
#include <XCEngine/Rendering/Passes/BuiltinSceneViewPostPassSequenceBuilder.h>
namespace {
using XCEngine::Rendering::Passes::BuiltinSceneViewPostPassSequenceBuilder;
using XCEngine::Rendering::Passes::BuiltinSceneViewPostPassSequenceInput;
using XCEngine::Rendering::Passes::ObjectIdOutlineStyle;
using XCEngine::Rendering::RenderPassSequence;
TEST(BuiltinSceneViewPostPassSequenceBuilder_Test, ReturnsInvalidSequenceWhenGridDataIsUnavailable) {
BuiltinSceneViewPostPassSequenceBuilder builder;
RenderPassSequence sequence;
const auto result = builder.Build({}, sequence);
EXPECT_FALSE(result.valid);
EXPECT_FALSE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 0u);
}
TEST(BuiltinSceneViewPostPassSequenceBuilder_Test, BuildsGridOnlySequenceWhenNothingIsSelected) {
BuiltinSceneViewPostPassSequenceBuilder builder;
RenderPassSequence sequence;
BuiltinSceneViewPostPassSequenceInput input = {};
input.gridPassData.valid = true;
const auto result = builder.Build(input, sequence);
EXPECT_TRUE(result.valid);
EXPECT_FALSE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 3u);
}
TEST(BuiltinSceneViewPostPassSequenceBuilder_Test, BuildsSelectionOutlineSequenceWhenObjectIdTextureViewExists) {
BuiltinSceneViewPostPassSequenceBuilder builder;
RenderPassSequence sequence;
BuiltinSceneViewPostPassSequenceInput input = {};
input.gridPassData.valid = true;
input.objectIdTextureView = reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1);
input.selectedObjectIds = { 7u, 11u };
const auto result = builder.Build(input, sequence);
EXPECT_TRUE(result.valid);
EXPECT_FALSE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 4u);
}
TEST(BuiltinSceneViewPostPassSequenceBuilder_Test, ReportsMissingObjectIdTextureViewAndFallsBackToGridOnlySequence) {
BuiltinSceneViewPostPassSequenceBuilder builder;
RenderPassSequence sequence;
BuiltinSceneViewPostPassSequenceInput input = {};
input.gridPassData.valid = true;
input.selectedObjectIds = { 42u };
const auto result = builder.Build(input, sequence);
EXPECT_TRUE(result.valid);
EXPECT_TRUE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 3u);
}
TEST(BuiltinSceneViewPostPassSequenceBuilder_Test, BuildsDebugMaskSequenceWhenRequested) {
BuiltinSceneViewPostPassSequenceBuilder builder;
RenderPassSequence sequence;
BuiltinSceneViewPostPassSequenceInput input = {};
input.gridPassData.valid = true;
input.objectIdTextureView = reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1);
input.selectedObjectIds = { 5u };
input.outlineStyle = ObjectIdOutlineStyle{};
input.outlineStyle.debugSelectionMask = true;
const auto result = builder.Build(input, sequence);
EXPECT_TRUE(result.valid);
EXPECT_FALSE(result.missingObjectIdTextureViewForSelection);
EXPECT_EQ(sequence.GetPassCount(), 3u);
}
} // namespace

View File

@@ -438,8 +438,8 @@ TEST(CameraRenderer_Test, ShutsDownSequencesWhenPostPassInitializationFails) {
"shutdown:pre" }));
}
TEST(CameraRenderer_Test, RejectsBuiltinSceneViewPostProcessThatCannotProduceFreshObjectIdData) {
Scene scene("CameraRendererSceneViewPostProcessValidationScene");
TEST(CameraRenderer_Test, RejectsBuiltinPostProcessThatCannotProduceFreshObjectIdData) {
Scene scene("CameraRendererBuiltinPostProcessValidationScene");
GameObject* cameraObject = scene.CreateGameObject("Camera");
auto* camera = cameraObject->AddComponent<CameraComponent>();
@@ -455,8 +455,8 @@ TEST(CameraRenderer_Test, RejectsBuiltinSceneViewPostProcessThatCannotProduceFre
request.context = CreateValidContext();
request.surface = RenderSurface(512, 512);
request.cameraDepth = camera->GetDepth();
request.builtinSceneViewPostProcess.gridPassData.valid = true;
request.builtinSceneViewPostProcess.objectIdTextureView =
request.builtinPostProcess.gridPassData.valid = true;
request.builtinPostProcess.objectIdTextureView =
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1);
EXPECT_FALSE(renderer.Render(request));

View File

@@ -22,6 +22,7 @@ using XCEngine::RHI::RHIResourceView;
using XCEngine::RHI::ResourceStates;
using XCEngine::RHI::ResourceViewDimension;
using XCEngine::RHI::ResourceViewType;
using XCEngine::Rendering::BuiltinPostProcessRequest;
using XCEngine::Rendering::RenderPass;
using XCEngine::Rendering::RenderPassContext;
using XCEngine::Rendering::RenderPassSequence;
@@ -127,25 +128,39 @@ TEST(ViewportRenderFlowUtilsTest, ApplyViewportFailureStatusRespectsSetIfEmptyBe
TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupAttachesOptionalPassesAndObjectIdSurface) {
DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView objectIdView(ResourceViewType::RenderTarget);
DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource);
ViewportRenderTargets targets = {};
targets.width = 800;
targets.height = 600;
targets.depthView = &depthView;
targets.objectIdView = &objectIdView;
targets.objectIdShaderView = &objectIdShaderView;
targets.objectIdState = ResourceStates::Common;
RenderPassSequence postPasses;
postPasses.AddPass(std::make_unique<NoopRenderPass>());
BuiltinPostProcessRequest builtinPostProcess = {};
builtinPostProcess.gridPassData.valid = true;
builtinPostProcess.selectedObjectIds = { 7u };
XCEngine::Rendering::CameraRenderRequest request = {};
request.surface = RenderSurface(800, 600);
request.surface.SetRenderArea(XCEngine::Math::RectInt(64, 32, 320, 240));
ApplySceneViewportRenderRequestSetup(targets, &postPasses, request);
ApplySceneViewportRenderRequestSetup(
targets,
&builtinPostProcess,
&postPasses,
request);
EXPECT_EQ(request.postScenePasses, &postPasses);
EXPECT_TRUE(request.objectId.IsRequested());
EXPECT_TRUE(request.builtinPostProcess.IsRequested());
EXPECT_EQ(request.builtinPostProcess.objectIdTextureView, &objectIdShaderView);
ASSERT_EQ(request.builtinPostProcess.selectedObjectIds.size(), 1u);
EXPECT_EQ(request.builtinPostProcess.selectedObjectIds[0], 7u);
ASSERT_EQ(request.objectId.surface.GetColorAttachments().size(), 1u);
EXPECT_EQ(request.objectId.surface.GetColorAttachments()[0], &objectIdView);
EXPECT_EQ(request.objectId.surface.GetDepthAttachment(), &depthView);
@@ -168,13 +183,15 @@ TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupSkipsUnavailableOp
XCEngine::Rendering::CameraRenderRequest request = {};
request.postScenePasses = reinterpret_cast<RenderPassSequence*>(static_cast<uintptr_t>(0x1));
request.objectId.surface = RenderSurface(1, 1);
request.builtinPostProcess.gridPassData.valid = true;
request.objectId.surface.SetColorAttachment(
reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x2)));
ApplySceneViewportRenderRequestSetup(targets, &postPasses, request);
ApplySceneViewportRenderRequestSetup(targets, nullptr, &postPasses, request);
EXPECT_EQ(request.postScenePasses, nullptr);
EXPECT_FALSE(request.objectId.IsRequested());
EXPECT_FALSE(request.builtinPostProcess.IsRequested());
}
TEST(ViewportRenderFlowUtilsTest, MarkSceneRenderSuccessMovesTargetsToShaderResourceState) {
@@ -191,7 +208,7 @@ TEST(ViewportRenderFlowUtilsTest, MarkSceneRenderSuccessMovesTargetsToShaderReso
XCEngine::Rendering::CameraRenderRequest request = {};
request.surface = RenderSurface(640, 360);
ApplySceneViewportRenderRequestSetup(targets, nullptr, request);
ApplySceneViewportRenderRequestSetup(targets, nullptr, nullptr, request);
MarkSceneViewportRenderSuccess(targets, request);
EXPECT_EQ(targets.colorState, ResourceStates::PixelShaderResource);