From 9edf3780854ffb1a96983f6df66b8e54c62d5f82 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Fri, 3 Apr 2026 14:26:36 +0800 Subject: [PATCH] Move scene viewport post effects into editor passes --- .../Viewport/Passes/SceneViewportGridPass.cpp | 53 +++++++++++++ .../Viewport/Passes/SceneViewportGridPass.h | 33 +++++++++ .../SceneViewportSelectionOutlinePass.cpp | 74 +++++++++++++++++++ .../SceneViewportSelectionOutlinePass.h | 39 ++++++++++ editor/src/Viewport/SceneViewportRenderPlan.h | 43 ++++++++--- .../Viewport/ViewportHostRenderFlowUtils.h | 14 +++- editor/src/Viewport/ViewportHostService.h | 21 ++++++ .../test_viewport_render_flow_utils.cpp | 52 ++++++++++++- 8 files changed, 314 insertions(+), 15 deletions(-) create mode 100644 editor/src/Viewport/Passes/SceneViewportGridPass.cpp create mode 100644 editor/src/Viewport/Passes/SceneViewportGridPass.h create mode 100644 editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp create mode 100644 editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.h diff --git a/editor/src/Viewport/Passes/SceneViewportGridPass.cpp b/editor/src/Viewport/Passes/SceneViewportGridPass.cpp new file mode 100644 index 00000000..8d3a9bda --- /dev/null +++ b/editor/src/Viewport/Passes/SceneViewportGridPass.cpp @@ -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 CreateSceneViewportGridPass( + SceneViewportGridPassRenderer& renderer, + const Rendering::Passes::InfiniteGridPassData& data) { + return std::make_unique(renderer, data); +} + +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/Viewport/Passes/SceneViewportGridPass.h b/editor/src/Viewport/Passes/SceneViewportGridPass.h new file mode 100644 index 00000000..cc809f06 --- /dev/null +++ b/editor/src/Viewport/Passes/SceneViewportGridPass.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +#include + +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 CreateSceneViewportGridPass( + SceneViewportGridPassRenderer& renderer, + const Rendering::Passes::InfiniteGridPassData& data); + +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp b/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp new file mode 100644 index 00000000..cacb2da8 --- /dev/null +++ b/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp @@ -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 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 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& selectedObjectIds, + const Rendering::Passes::ObjectIdOutlineStyle& style) { + return m_outlinePass.Render( + renderContext, + surface, + objectIdTextureView, + selectedObjectIds, + style); +} + +std::unique_ptr CreateSceneViewportSelectionOutlinePass( + SceneViewportSelectionOutlinePassRenderer& renderer, + RHI::RHIResourceView* objectIdTextureView, + const std::vector& selectedObjectIds, + const Rendering::Passes::ObjectIdOutlineStyle& style) { + return std::make_unique( + renderer, + objectIdTextureView, + selectedObjectIds, + style); +} + +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.h b/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.h new file mode 100644 index 00000000..da388c86 --- /dev/null +++ b/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +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& selectedObjectIds, + const Rendering::Passes::ObjectIdOutlineStyle& style); + +private: + Rendering::Passes::BuiltinObjectIdOutlinePass m_outlinePass; +}; + +std::unique_ptr CreateSceneViewportSelectionOutlinePass( + SceneViewportSelectionOutlinePassRenderer& renderer, + RHI::RHIResourceView* objectIdTextureView, + const std::vector& selectedObjectIds, + const Rendering::Passes::ObjectIdOutlineStyle& style); + +} // namespace Editor +} // namespace XCEngine diff --git a/editor/src/Viewport/SceneViewportRenderPlan.h b/editor/src/Viewport/SceneViewportRenderPlan.h index e7e4ecc3..5bbb70b1 100644 --- a/editor/src/Viewport/SceneViewportRenderPlan.h +++ b/editor/src/Viewport/SceneViewportRenderPlan.h @@ -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(const SceneViewportOverlayFrameData&)>; +using SceneViewportGridPassFactory = + std::function(const Rendering::Passes::InfiniteGridPassData&)>; +using SceneViewportSelectionOutlinePassFactory = std::function( + RHI::RHIResourceView*, + const std::vector&, + const Rendering::Passes::ObjectIdOutlineStyle&)>; struct SceneViewportRenderPlanBuildResult { SceneViewportRenderPlan plan = {}; @@ -45,6 +53,8 @@ inline SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan( const std::vector& 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 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 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); diff --git a/editor/src/Viewport/ViewportHostRenderFlowUtils.h b/editor/src/Viewport/ViewportHostRenderFlowUtils.h index 751dccfd..61b7a5f4 100644 --- a/editor/src/Viewport/ViewportHostRenderFlowUtils.h +++ b/editor/src/Viewport/ViewportHostRenderFlowUtils.h @@ -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& 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 && diff --git a/editor/src/Viewport/ViewportHostService.h b/editor/src/Viewport/ViewportHostService.h index 91e5242a..f9e0918f 100644 --- a/editor/src/Viewport/ViewportHostService.h +++ b/editor/src/Viewport/ViewportHostService.h @@ -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& 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; }; diff --git a/tests/editor/test_viewport_render_flow_utils.cpp b/tests/editor/test_viewport_render_flow_utils.cpp index 46aa4547..a71f23b2 100644 --- a/tests/editor/test_viewport_render_flow_utils.cpp +++ b/tests/editor/test_viewport_render_flow_utils.cpp @@ -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(); + }, + [&selectionOutlinePassFactoryCallCount]( + RHIResourceView* objectIdTextureView, + const std::vector& 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(); + }, [&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(); + }, + []( + RHIResourceView*, + const std::vector&, + const XCEngine::Rendering::Passes::ObjectIdOutlineStyle&) { + return std::make_unique(); + }, + [](const SceneViewportOverlayFrameData&) { + return std::make_unique(); + }, + 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);