Move scene viewport post effects into editor passes

This commit is contained in:
2026-04-03 14:26:36 +08:00
parent b882610bbc
commit 9edf378085
8 changed files with 314 additions and 15 deletions

View File

@@ -0,0 +1,53 @@
#include "Passes/SceneViewportGridPass.h"
namespace XCEngine {
namespace Editor {
namespace {
class SceneViewportGridPass final : public Rendering::RenderPass {
public:
SceneViewportGridPass(
SceneViewportGridPassRenderer& renderer,
const Rendering::Passes::InfiniteGridPassData& data)
: m_renderer(renderer)
, m_data(data) {
}
const char* GetName() const override {
return "SceneViewportGrid";
}
bool Execute(const Rendering::RenderPassContext& context) override {
return m_renderer.Render(
context.renderContext,
context.surface,
m_data);
}
private:
SceneViewportGridPassRenderer& m_renderer;
Rendering::Passes::InfiniteGridPassData m_data = {};
};
} // namespace
void SceneViewportGridPassRenderer::Shutdown() {
m_gridPass.Shutdown();
}
bool SceneViewportGridPassRenderer::Render(
const Rendering::RenderContext& renderContext,
const Rendering::RenderSurface& surface,
const Rendering::Passes::InfiniteGridPassData& data) {
return m_gridPass.Render(renderContext, surface, data);
}
std::unique_ptr<Rendering::RenderPass> CreateSceneViewportGridPass(
SceneViewportGridPassRenderer& renderer,
const Rendering::Passes::InfiniteGridPassData& data) {
return std::make_unique<SceneViewportGridPass>(renderer, data);
}
} // namespace Editor
} // namespace XCEngine

View File

@@ -0,0 +1,33 @@
#pragma once
#include <XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h>
#include <XCEngine/Rendering/RenderContext.h>
#include <XCEngine/Rendering/RenderPass.h>
#include <XCEngine/Rendering/RenderSurface.h>
#include <memory>
namespace XCEngine {
namespace Editor {
class SceneViewportGridPassRenderer {
public:
~SceneViewportGridPassRenderer() = default;
void Shutdown();
bool Render(
const Rendering::RenderContext& renderContext,
const Rendering::RenderSurface& surface,
const Rendering::Passes::InfiniteGridPassData& data);
private:
Rendering::Passes::BuiltinInfiniteGridPass m_gridPass;
};
std::unique_ptr<Rendering::RenderPass> CreateSceneViewportGridPass(
SceneViewportGridPassRenderer& renderer,
const Rendering::Passes::InfiniteGridPassData& data);
} // namespace Editor
} // namespace XCEngine

View File

@@ -0,0 +1,74 @@
#include "Passes/SceneViewportSelectionOutlinePass.h"
namespace XCEngine {
namespace Editor {
namespace {
class SceneViewportSelectionOutlinePass final : public Rendering::RenderPass {
public:
SceneViewportSelectionOutlinePass(
SceneViewportSelectionOutlinePassRenderer& renderer,
RHI::RHIResourceView* objectIdTextureView,
std::vector<uint64_t> selectedObjectIds,
const Rendering::Passes::ObjectIdOutlineStyle& style)
: m_renderer(renderer)
, m_objectIdTextureView(objectIdTextureView)
, m_selectedObjectIds(std::move(selectedObjectIds))
, m_style(style) {
}
const char* GetName() const override {
return "SceneViewportSelectionOutline";
}
bool Execute(const Rendering::RenderPassContext& context) override {
return m_renderer.Render(
context.renderContext,
context.surface,
m_objectIdTextureView,
m_selectedObjectIds,
m_style);
}
private:
SceneViewportSelectionOutlinePassRenderer& m_renderer;
RHI::RHIResourceView* m_objectIdTextureView = nullptr;
std::vector<uint64_t> m_selectedObjectIds = {};
Rendering::Passes::ObjectIdOutlineStyle m_style = {};
};
} // namespace
void SceneViewportSelectionOutlinePassRenderer::Shutdown() {
m_outlinePass.Shutdown();
}
bool SceneViewportSelectionOutlinePassRenderer::Render(
const Rendering::RenderContext& renderContext,
const Rendering::RenderSurface& surface,
RHI::RHIResourceView* objectIdTextureView,
const std::vector<uint64_t>& selectedObjectIds,
const Rendering::Passes::ObjectIdOutlineStyle& style) {
return m_outlinePass.Render(
renderContext,
surface,
objectIdTextureView,
selectedObjectIds,
style);
}
std::unique_ptr<Rendering::RenderPass> CreateSceneViewportSelectionOutlinePass(
SceneViewportSelectionOutlinePassRenderer& renderer,
RHI::RHIResourceView* objectIdTextureView,
const std::vector<uint64_t>& selectedObjectIds,
const Rendering::Passes::ObjectIdOutlineStyle& style) {
return std::make_unique<SceneViewportSelectionOutlinePass>(
renderer,
objectIdTextureView,
selectedObjectIds,
style);
}
} // namespace Editor
} // namespace XCEngine

View File

@@ -0,0 +1,39 @@
#pragma once
#include <XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h>
#include <XCEngine/Rendering/RenderContext.h>
#include <XCEngine/Rendering/RenderPass.h>
#include <XCEngine/Rendering/RenderSurface.h>
#include <cstdint>
#include <memory>
#include <vector>
namespace XCEngine {
namespace Editor {
class SceneViewportSelectionOutlinePassRenderer {
public:
~SceneViewportSelectionOutlinePassRenderer() = default;
void Shutdown();
bool Render(
const Rendering::RenderContext& renderContext,
const Rendering::RenderSurface& surface,
RHI::RHIResourceView* objectIdTextureView,
const std::vector<uint64_t>& selectedObjectIds,
const Rendering::Passes::ObjectIdOutlineStyle& style);
private:
Rendering::Passes::BuiltinObjectIdOutlinePass m_outlinePass;
};
std::unique_ptr<Rendering::RenderPass> CreateSceneViewportSelectionOutlinePass(
SceneViewportSelectionOutlinePassRenderer& renderer,
RHI::RHIResourceView* objectIdTextureView,
const std::vector<uint64_t>& selectedObjectIds,
const Rendering::Passes::ObjectIdOutlineStyle& style);
} // namespace Editor
} // namespace XCEngine

View File

@@ -1,5 +1,7 @@
#pragma once
#include "Passes/SceneViewportGridPass.h"
#include "Passes/SceneViewportSelectionOutlinePass.h"
#include "Passes/SceneViewportEditorOverlayPass.h"
#include "SceneViewportEditorOverlayData.h"
#include "ViewportHostRenderFlowUtils.h"
@@ -33,6 +35,12 @@ struct SceneViewportRenderPlan {
using SceneViewportOverlayPassFactory =
std::function<std::unique_ptr<Rendering::RenderPass>(const SceneViewportOverlayFrameData&)>;
using SceneViewportGridPassFactory =
std::function<std::unique_ptr<Rendering::RenderPass>(const Rendering::Passes::InfiniteGridPassData&)>;
using SceneViewportSelectionOutlinePassFactory = std::function<std::unique_ptr<Rendering::RenderPass>(
RHI::RHIResourceView*,
const std::vector<uint64_t>&,
const Rendering::Passes::ObjectIdOutlineStyle&)>;
struct SceneViewportRenderPlanBuildResult {
SceneViewportRenderPlan plan = {};
@@ -45,6 +53,8 @@ inline SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan(
const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportOverlayFrameData& editorOverlayFrameData,
const SceneViewportOverlayFrameData& transientOverlayFrameData,
const SceneViewportGridPassFactory& gridPassFactory,
const SceneViewportSelectionOutlinePassFactory& selectionOutlinePassFactory,
const SceneViewportOverlayPassFactory& overlayPassFactory,
bool debugSelectionMask = false) {
SceneViewportRenderPlanBuildResult result = {};
@@ -52,14 +62,29 @@ inline SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan(
return result;
}
const SceneViewportBuiltinPostProcessBuildResult builtinPostProcess =
BuildSceneViewportBuiltinPostProcess(
overlay,
selectedObjectIds,
targets.objectIdShaderView != nullptr,
debugSelectionMask);
result.plan.builtinPostProcess = builtinPostProcess.request;
result.warningStatusText = builtinPostProcess.warningStatusText;
const Rendering::Passes::InfiniteGridPassData gridPassData = BuildSceneViewportGridPassData(overlay);
if (gridPassData.valid && gridPassFactory != nullptr) {
std::unique_ptr<Rendering::RenderPass> gridPass = gridPassFactory(gridPassData);
if (gridPass != nullptr) {
result.plan.postScenePasses.AddPass(std::move(gridPass));
}
}
if (!selectedObjectIds.empty()) {
if (targets.objectIdShaderView != nullptr &&
selectionOutlinePassFactory != nullptr) {
std::unique_ptr<Rendering::RenderPass> selectionOutlinePass =
selectionOutlinePassFactory(
targets.objectIdShaderView,
selectedObjectIds,
BuildSceneViewportSelectionOutlineStyle(debugSelectionMask));
if (selectionOutlinePass != nullptr) {
result.plan.postScenePasses.AddPass(std::move(selectionOutlinePass));
}
} else if (!debugSelectionMask) {
result.warningStatusText = "Scene object id shader view is unavailable";
}
}
SceneViewportOverlayFrameData renderOverlayFrameData = editorOverlayFrameData;
AppendSceneViewportOverlayFrameData(renderOverlayFrameData, transientOverlayFrameData);
@@ -80,7 +105,7 @@ inline void ApplySceneViewportRenderPlan(
Rendering::CameraRenderRequest& request) {
ApplySceneViewportRenderRequestSetup(
targets,
&plan.builtinPostProcess,
plan.builtinPostProcess.IsRequested() ? &plan.builtinPostProcess : nullptr,
&plan.postScenePasses,
request);

View File

@@ -135,6 +135,15 @@ inline Rendering::Passes::InfiniteGridPassData BuildSceneViewportGridPassData(
return data;
}
inline Rendering::Passes::ObjectIdOutlineStyle BuildSceneViewportSelectionOutlineStyle(
bool debugSelectionMask = false) {
Rendering::Passes::ObjectIdOutlineStyle style = {};
style.outlineColor = Math::Color(1.0f, 0.4f, 0.0f, 1.0f);
style.outlineWidthPixels = 2.0f;
style.debugSelectionMask = debugSelectionMask;
return style;
}
inline SceneViewportBuiltinPostProcessBuildResult BuildSceneViewportBuiltinPostProcess(
const SceneViewportOverlayData& overlay,
const std::vector<uint64_t>& selectedObjectIds,
@@ -147,10 +156,7 @@ inline SceneViewportBuiltinPostProcessBuildResult BuildSceneViewportBuiltinPostP
result.request.gridPassData = BuildSceneViewportGridPassData(overlay);
result.request.selectedObjectIds = selectedObjectIds;
result.request.outlineStyle = {};
result.request.outlineStyle.outlineColor = Math::Color(1.0f, 0.4f, 0.0f, 1.0f);
result.request.outlineStyle.outlineWidthPixels = 2.0f;
result.request.outlineStyle.debugSelectionMask = debugSelectionMask;
result.request.outlineStyle = BuildSceneViewportSelectionOutlineStyle(debugSelectionMask);
if (!selectedObjectIds.empty() &&
!debugSelectionMask &&

View File

@@ -5,6 +5,8 @@
#include "Core/ISelectionManager.h"
#include "IViewportHostService.h"
#include "Passes/SceneViewportEditorOverlayPass.h"
#include "Passes/SceneViewportGridPass.h"
#include "Passes/SceneViewportSelectionOutlinePass.h"
#include "SceneViewportCameraController.h"
#include "SceneViewportEditorOverlayData.h"
#include "SceneViewportOverlayHandleBuilder.h"
@@ -206,6 +208,8 @@ public:
entry = {};
}
m_sceneViewportSelectionOutlineRenderer.Shutdown();
m_sceneViewportGridRenderer.Shutdown();
m_sceneViewportEditorOverlayRenderer.Shutdown();
m_sceneViewCamera = {};
ResetSceneViewEditorOverlayFrameData();
@@ -650,6 +654,21 @@ private:
selectedObjectIds,
editorOverlayFrameData,
BuildSceneViewTransientTransformGizmoOverlayFrameData(),
[this](const Rendering::Passes::InfiniteGridPassData& data) {
return CreateSceneViewportGridPass(
m_sceneViewportGridRenderer,
data);
},
[this](
RHI::RHIResourceView* objectIdTextureView,
const std::vector<uint64_t>& selectedObjectIds,
const Rendering::Passes::ObjectIdOutlineStyle& style) {
return CreateSceneViewportSelectionOutlinePass(
m_sceneViewportSelectionOutlineRenderer,
objectIdTextureView,
selectedObjectIds,
style);
},
[this](const SceneViewportOverlayFrameData& frameData) {
return CreateSceneViewportEditorOverlayPass(
m_sceneViewportEditorOverlayRenderer,
@@ -867,6 +886,8 @@ private:
uint32_t m_sceneViewEditorOverlayViewportHeight = 0u;
uint64_t m_sceneViewEditorOverlayContentSignature = 0u;
bool m_sceneViewEditorOverlayCached = false;
SceneViewportGridPassRenderer m_sceneViewportGridRenderer;
SceneViewportSelectionOutlinePassRenderer m_sceneViewportSelectionOutlineRenderer;
SceneViewportEditorOverlayPassRenderer m_sceneViewportEditorOverlayRenderer;
};

View File

@@ -16,6 +16,7 @@ using XCEngine::Editor::BuildGameViewportRenderFailurePolicy;
using XCEngine::Editor::BuildSceneViewportBuiltinPostProcess;
using XCEngine::Editor::BuildSceneViewportRenderPlan;
using XCEngine::Editor::BuildSceneViewportRenderFailurePolicy;
using XCEngine::Editor::BuildSceneViewportSelectionOutlineStyle;
using XCEngine::Editor::BuildViewportRenderTargetUnavailablePolicy;
using XCEngine::Editor::GameViewportRenderFailure;
using XCEngine::Editor::MarkGameViewportRenderSuccess;
@@ -314,7 +315,7 @@ TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupPreservesBuiltinGr
EXPECT_TRUE(request.objectId.IsRequested());
}
TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsBuiltinAndOverlayPasses) {
TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsPostSceneAndOverlayPasses) {
DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource);
ViewportRenderTargets targets = {};
@@ -334,12 +335,29 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsBuiltinAnd
size_t factoryCallCount = 0u;
size_t combinedWorldLineCount = 0u;
size_t gridPassFactoryCallCount = 0u;
size_t selectionOutlinePassFactoryCallCount = 0u;
const auto result = BuildSceneViewportRenderPlan(
targets,
overlay,
{ 7u, 11u },
editorOverlayFrameData,
transientOverlayFrameData,
[&gridPassFactoryCallCount](const XCEngine::Rendering::Passes::InfiniteGridPassData& data) {
++gridPassFactoryCallCount;
EXPECT_TRUE(data.valid);
return std::make_unique<NoopRenderPass>();
},
[&selectionOutlinePassFactoryCallCount](
RHIResourceView* objectIdTextureView,
const std::vector<uint64_t>& selectedObjectIds,
const XCEngine::Rendering::Passes::ObjectIdOutlineStyle& style) {
++selectionOutlinePassFactoryCallCount;
EXPECT_NE(objectIdTextureView, nullptr);
EXPECT_EQ(selectedObjectIds.size(), 2u);
EXPECT_FLOAT_EQ(style.outlineWidthPixels, 2.0f);
return std::make_unique<NoopRenderPass>();
},
[&factoryCallCount, &combinedWorldLineCount](const SceneViewportOverlayFrameData& frameData) {
++factoryCallCount;
combinedWorldLineCount = frameData.worldLines.size();
@@ -347,13 +365,43 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsBuiltinAnd
},
false);
EXPECT_TRUE(result.plan.builtinPostProcess.IsRequested());
EXPECT_FALSE(result.plan.builtinPostProcess.IsRequested());
EXPECT_EQ(result.plan.postScenePasses.GetPassCount(), 2u);
EXPECT_EQ(result.plan.overlayPasses.GetPassCount(), 1u);
EXPECT_EQ(factoryCallCount, 1u);
EXPECT_EQ(gridPassFactoryCallCount, 1u);
EXPECT_EQ(selectionOutlinePassFactoryCallCount, 1u);
EXPECT_EQ(combinedWorldLineCount, 2u);
EXPECT_EQ(result.warningStatusText, nullptr);
}
TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanWarnsWhenSelectionOutlineCannotAccessObjectIdTexture) {
const SceneViewportOverlayData overlay = CreateValidOverlay();
const auto result = BuildSceneViewportRenderPlan(
{},
overlay,
{ 42u },
{},
{},
[](const XCEngine::Rendering::Passes::InfiniteGridPassData&) {
return std::make_unique<NoopRenderPass>();
},
[](
RHIResourceView*,
const std::vector<uint64_t>&,
const XCEngine::Rendering::Passes::ObjectIdOutlineStyle&) {
return std::make_unique<NoopRenderPass>();
},
[](const SceneViewportOverlayFrameData&) {
return std::make_unique<NoopRenderPass>();
},
false);
EXPECT_EQ(result.plan.postScenePasses.GetPassCount(), 1u);
EXPECT_STREQ(result.warningStatusText, "Scene object id shader view is unavailable");
}
TEST(ViewportRenderFlowUtilsTest, ApplySceneViewportRenderPlanAttachesPlannedPassesAndClearState) {
DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView objectIdView(ResourceViewType::RenderTarget);