refactor: route scene view passes through camera renderer

This commit is contained in:
2026-04-02 12:47:06 +08:00
parent 0d3851204f
commit 697deb4e41
6 changed files with 107 additions and 29 deletions

View File

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

View File

@@ -19,7 +19,6 @@
#include <XCEngine/RHI/RHIResourceView.h> #include <XCEngine/RHI/RHIResourceView.h>
#include <XCEngine/RHI/RHITexture.h> #include <XCEngine/RHI/RHITexture.h>
#include <XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h> #include <XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h>
#include <XCEngine/Rendering/Passes/BuiltinSceneViewPostPassSequenceBuilder.h>
#include <XCEngine/Rendering/RenderContext.h> #include <XCEngine/Rendering/RenderContext.h>
#include <XCEngine/Rendering/RenderSurface.h> #include <XCEngine/Rendering/RenderSurface.h>
#include <XCEngine/Rendering/SceneRenderer.h> #include <XCEngine/Rendering/SceneRenderer.h>
@@ -92,7 +91,6 @@ public:
m_sceneViewLastRenderContext = {}; m_sceneViewLastRenderContext = {};
m_device = nullptr; m_device = nullptr;
m_backend = nullptr; m_backend = nullptr;
m_sceneViewPostPassBuilder.Shutdown();
m_sceneRenderer.reset(); m_sceneRenderer.reset();
} }
@@ -280,7 +278,7 @@ private:
struct SceneViewportRenderState { struct SceneViewportRenderState {
SceneViewportOverlayData overlay = {}; SceneViewportOverlayData overlay = {};
Rendering::RenderPassSequence postPasses; Rendering::BuiltinSceneViewPostProcessRequest builtinSceneViewPostProcess = {};
std::vector<uint64_t> selectedObjectIds; std::vector<uint64_t> selectedObjectIds;
}; };
@@ -412,32 +410,28 @@ private:
policy.clearColor.a); policy.clearColor.a);
} }
bool BuildSceneViewPostPassSequence( void BuildSceneViewBuiltinPostProcessRequest(
ViewportEntry& entry, ViewportEntry& entry,
const SceneViewportOverlayData& overlay, const SceneViewportOverlayData& overlay,
const std::vector<uint64_t>& selectedObjectIds, const std::vector<uint64_t>& selectedObjectIds,
Rendering::RenderPassSequence& outPostPasses) { Rendering::BuiltinSceneViewPostProcessRequest& outRequest) {
Rendering::Passes::ObjectIdOutlineStyle outlineStyle = {}; if (!overlay.valid) {
outlineStyle.outlineColor = Math::Color(1.0f, 0.4f, 0.0f, 1.0f); outRequest = {};
outlineStyle.outlineWidthPixels = 2.0f; return;
outlineStyle.debugSelectionMask = kDebugSceneSelectionMask;
const Rendering::Passes::BuiltinSceneViewPostPassSequenceBuildResult result =
m_sceneViewPostPassBuilder.Build(
{
BuildInfiniteGridPassData(overlay),
entry.renderTargets.objectIdShaderView,
selectedObjectIds,
outlineStyle
},
outPostPasses);
if (result.missingObjectIdTextureViewForSelection &&
!kDebugSceneSelectionMask) {
SetViewportStatusIfEmpty(entry.statusText, "Scene object id shader view is unavailable");
} }
return result.valid; outRequest.gridPassData = BuildInfiniteGridPassData(overlay);
outRequest.selectedObjectIds = selectedObjectIds;
outRequest.outlineStyle = {};
outRequest.outlineStyle.outlineColor = Math::Color(1.0f, 0.4f, 0.0f, 1.0f);
outRequest.outlineStyle.outlineWidthPixels = 2.0f;
outRequest.outlineStyle.debugSelectionMask = kDebugSceneSelectionMask;
if (!selectedObjectIds.empty() &&
!kDebugSceneSelectionMask &&
entry.renderTargets.objectIdShaderView == nullptr) {
SetViewportStatusIfEmpty(entry.statusText, "Scene object id shader view is unavailable");
}
} }
void BuildSceneViewportRenderState( void BuildSceneViewportRenderState(
@@ -452,12 +446,11 @@ private:
} }
outState.selectedObjectIds = context.GetSelectionManager().GetSelectedEntities(); outState.selectedObjectIds = context.GetSelectionManager().GetSelectedEntities();
BuildSceneViewBuiltinPostProcessRequest(
BuildSceneViewPostPassSequence(
entry, entry,
outState.overlay, outState.overlay,
outState.selectedObjectIds, outState.selectedObjectIds,
outState.postPasses); outState.builtinSceneViewPostProcess);
} }
bool RenderSceneViewportEntry( bool RenderSceneViewportEntry(
@@ -502,7 +495,8 @@ private:
ApplySceneViewportRenderRequestSetup( ApplySceneViewportRenderRequestSetup(
entry.renderTargets, entry.renderTargets,
&sceneState.postPasses, &sceneState.builtinSceneViewPostProcess,
nullptr,
requests[0]); requests[0]);
requests[0].hasClearColorOverride = true; requests[0].hasClearColorOverride = true;
requests[0].clearColorOverride = Math::Color(0.27f, 0.27f, 0.27f, 1.0f); requests[0].clearColorOverride = Math::Color(0.27f, 0.27f, 0.27f, 1.0f);
@@ -657,7 +651,6 @@ private:
Rendering::RenderContext m_sceneViewLastRenderContext = {}; Rendering::RenderContext m_sceneViewLastRenderContext = {};
std::array<ViewportEntry, 2> m_entries = {}; std::array<ViewportEntry, 2> m_entries = {};
SceneViewCameraState m_sceneViewCamera; SceneViewCameraState m_sceneViewCamera;
Rendering::Passes::BuiltinSceneViewPostPassSequenceBuilder m_sceneViewPostPassBuilder;
}; };
} // namespace Editor } // namespace Editor

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h>
#include <XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h>
#include <XCEngine/Rendering/RenderPass.h> #include <XCEngine/Rendering/RenderPass.h>
#include <XCEngine/Rendering/RenderCameraData.h> #include <XCEngine/Rendering/RenderCameraData.h>
#include <XCEngine/Rendering/RenderContext.h> #include <XCEngine/Rendering/RenderContext.h>
@@ -30,12 +32,24 @@ struct ObjectIdRenderRequest {
} }
}; };
struct BuiltinSceneViewPostProcessRequest {
Passes::InfiniteGridPassData gridPassData = {};
RHI::RHIResourceView* objectIdTextureView = nullptr;
std::vector<uint64_t> selectedObjectIds = {};
Passes::ObjectIdOutlineStyle outlineStyle = {};
bool IsRequested() const {
return gridPassData.valid;
}
};
struct CameraRenderRequest { struct CameraRenderRequest {
const Components::Scene* scene = nullptr; const Components::Scene* scene = nullptr;
Components::CameraComponent* camera = nullptr; Components::CameraComponent* camera = nullptr;
RenderContext context; RenderContext context;
RenderSurface surface; RenderSurface surface;
ObjectIdRenderRequest objectId; ObjectIdRenderRequest objectId;
BuiltinSceneViewPostProcessRequest builtinSceneViewPostProcess;
float cameraDepth = 0.0f; float cameraDepth = 0.0f;
uint8_t cameraStackOrder = 0; uint8_t cameraStackOrder = 0;
RenderClearFlags clearFlags = RenderClearFlags::All; RenderClearFlags clearFlags = RenderClearFlags::All;

View File

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

View File

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

View File

@@ -438,6 +438,31 @@ TEST(CameraRenderer_Test, ShutsDownSequencesWhenPostPassInitializationFails) {
"shutdown:pre" })); "shutdown:pre" }));
} }
TEST(CameraRenderer_Test, RejectsBuiltinSceneViewPostProcessThatCannotProduceFreshObjectIdData) {
Scene scene("CameraRendererSceneViewPostProcessValidationScene");
GameObject* cameraObject = scene.CreateGameObject("Camera");
auto* camera = cameraObject->AddComponent<CameraComponent>();
camera->SetPrimary(true);
camera->SetDepth(4.0f);
auto state = std::make_shared<MockPipelineState>();
CameraRenderer renderer(std::make_unique<MockPipeline>(state));
CameraRenderRequest request;
request.scene = &scene;
request.camera = camera;
request.context = CreateValidContext();
request.surface = RenderSurface(512, 512);
request.cameraDepth = camera->GetDepth();
request.builtinSceneViewPostProcess.gridPassData.valid = true;
request.builtinSceneViewPostProcess.objectIdTextureView =
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1);
EXPECT_FALSE(renderer.Render(request));
EXPECT_TRUE(state->eventLog.empty());
}
TEST(SceneRenderer_Test, BuildsSortedRequestsForAllUsableCamerasAndHonorsOverrideCamera) { TEST(SceneRenderer_Test, BuildsSortedRequestsForAllUsableCamerasAndHonorsOverrideCamera) {
Scene scene("SceneRendererRequestScene"); Scene scene("SceneRendererRequestScene");