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

View File

@@ -135,6 +135,15 @@ inline Rendering::Passes::InfiniteGridPassData BuildSceneViewportGridPassData(
return data; 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( inline SceneViewportBuiltinPostProcessBuildResult BuildSceneViewportBuiltinPostProcess(
const SceneViewportOverlayData& overlay, const SceneViewportOverlayData& overlay,
const std::vector<uint64_t>& selectedObjectIds, const std::vector<uint64_t>& selectedObjectIds,
@@ -147,10 +156,7 @@ inline SceneViewportBuiltinPostProcessBuildResult BuildSceneViewportBuiltinPostP
result.request.gridPassData = BuildSceneViewportGridPassData(overlay); result.request.gridPassData = BuildSceneViewportGridPassData(overlay);
result.request.selectedObjectIds = selectedObjectIds; result.request.selectedObjectIds = selectedObjectIds;
result.request.outlineStyle = {}; result.request.outlineStyle = BuildSceneViewportSelectionOutlineStyle(debugSelectionMask);
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;
if (!selectedObjectIds.empty() && if (!selectedObjectIds.empty() &&
!debugSelectionMask && !debugSelectionMask &&

View File

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

View File

@@ -16,6 +16,7 @@ using XCEngine::Editor::BuildGameViewportRenderFailurePolicy;
using XCEngine::Editor::BuildSceneViewportBuiltinPostProcess; using XCEngine::Editor::BuildSceneViewportBuiltinPostProcess;
using XCEngine::Editor::BuildSceneViewportRenderPlan; using XCEngine::Editor::BuildSceneViewportRenderPlan;
using XCEngine::Editor::BuildSceneViewportRenderFailurePolicy; using XCEngine::Editor::BuildSceneViewportRenderFailurePolicy;
using XCEngine::Editor::BuildSceneViewportSelectionOutlineStyle;
using XCEngine::Editor::BuildViewportRenderTargetUnavailablePolicy; using XCEngine::Editor::BuildViewportRenderTargetUnavailablePolicy;
using XCEngine::Editor::GameViewportRenderFailure; using XCEngine::Editor::GameViewportRenderFailure;
using XCEngine::Editor::MarkGameViewportRenderSuccess; using XCEngine::Editor::MarkGameViewportRenderSuccess;
@@ -314,7 +315,7 @@ TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupPreservesBuiltinGr
EXPECT_TRUE(request.objectId.IsRequested()); EXPECT_TRUE(request.objectId.IsRequested());
} }
TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsBuiltinAndOverlayPasses) { TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsPostSceneAndOverlayPasses) {
DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource); DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource);
ViewportRenderTargets targets = {}; ViewportRenderTargets targets = {};
@@ -334,12 +335,29 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsBuiltinAnd
size_t factoryCallCount = 0u; size_t factoryCallCount = 0u;
size_t combinedWorldLineCount = 0u; size_t combinedWorldLineCount = 0u;
size_t gridPassFactoryCallCount = 0u;
size_t selectionOutlinePassFactoryCallCount = 0u;
const auto result = BuildSceneViewportRenderPlan( const auto result = BuildSceneViewportRenderPlan(
targets, targets,
overlay, overlay,
{ 7u, 11u }, { 7u, 11u },
editorOverlayFrameData, editorOverlayFrameData,
transientOverlayFrameData, 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](const SceneViewportOverlayFrameData& frameData) {
++factoryCallCount; ++factoryCallCount;
combinedWorldLineCount = frameData.worldLines.size(); combinedWorldLineCount = frameData.worldLines.size();
@@ -347,13 +365,43 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsBuiltinAnd
}, },
false); 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(result.plan.overlayPasses.GetPassCount(), 1u);
EXPECT_EQ(factoryCallCount, 1u); EXPECT_EQ(factoryCallCount, 1u);
EXPECT_EQ(gridPassFactoryCallCount, 1u);
EXPECT_EQ(selectionOutlinePassFactoryCallCount, 1u);
EXPECT_EQ(combinedWorldLineCount, 2u); EXPECT_EQ(combinedWorldLineCount, 2u);
EXPECT_EQ(result.warningStatusText, nullptr); 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) { TEST(ViewportRenderFlowUtilsTest, ApplySceneViewportRenderPlanAttachesPlannedPassesAndClearState) {
DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt); DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView objectIdView(ResourceViewType::RenderTarget); DummyResourceView objectIdView(ResourceViewType::RenderTarget);