refactor(rendering): formalize camera frame stage contracts

This commit is contained in:
2026-04-15 15:14:06 +08:00
parent 3937badf37
commit 795eaf80df
16 changed files with 558 additions and 296 deletions

View File

@@ -102,31 +102,31 @@ bool CameraFramePlan::IsFinalOutputStageValid() const {
}
bool CameraFramePlan::HasFrameStage(CameraFrameStage stage) const {
switch (stage) {
case CameraFrameStage::PreScenePasses:
return preScenePasses != nullptr;
case CameraFrameStage::ShadowCaster:
return shadowCaster.IsRequested() || directionalShadow.IsValid();
case CameraFrameStage::DepthOnly:
return request.depthOnly.IsRequested();
case CameraFrameStage::MainScene:
if (stage == CameraFrameStage::MainScene) {
return true;
case CameraFrameStage::PostProcess:
return postProcess.IsRequested();
case CameraFrameStage::FinalOutput:
return finalOutput.IsRequested();
case CameraFrameStage::ObjectId:
}
if (IsCameraFrameSequenceStage(stage)) {
return GetPassSequence(stage) != nullptr;
}
switch (GetCameraFrameStageRequestKind(stage)) {
case CameraFrameStageRequestKind::ShadowCaster:
return shadowCaster.IsRequested() || directionalShadow.IsValid();
case CameraFrameStageRequestKind::DepthOnly:
return request.depthOnly.IsRequested();
case CameraFrameStageRequestKind::ObjectId:
return request.objectId.IsRequested();
case CameraFrameStage::PostScenePasses:
return postScenePasses != nullptr;
case CameraFrameStage::OverlayPasses:
return overlayPasses != nullptr;
default:
return false;
}
}
RenderPassSequence* CameraFramePlan::GetPassSequence(CameraFrameStage stage) const {
if (!IsCameraFrameSequenceStage(stage)) {
return nullptr;
}
switch (stage) {
case CameraFrameStage::PreScenePasses:
return preScenePasses;
@@ -144,10 +144,10 @@ RenderPassSequence* CameraFramePlan::GetPassSequence(CameraFrameStage stage) con
}
const ScenePassRenderRequest* CameraFramePlan::GetScenePassRequest(CameraFrameStage stage) const {
switch (stage) {
case CameraFrameStage::ShadowCaster:
switch (GetCameraFrameStageRequestKind(stage)) {
case CameraFrameStageRequestKind::ShadowCaster:
return &shadowCaster;
case CameraFrameStage::DepthOnly:
case CameraFrameStageRequestKind::DepthOnly:
return &request.depthOnly;
default:
return nullptr;
@@ -155,7 +155,10 @@ const ScenePassRenderRequest* CameraFramePlan::GetScenePassRequest(CameraFrameSt
}
const ObjectIdRenderRequest* CameraFramePlan::GetObjectIdRequest(CameraFrameStage stage) const {
return stage == CameraFrameStage::ObjectId ? &request.objectId : nullptr;
return GetCameraFrameStageRequestKind(stage) ==
CameraFrameStageRequestKind::ObjectId
? &request.objectId
: nullptr;
}
const RenderSurface& CameraFramePlan::GetMainSceneSurface() const {

View File

@@ -3,10 +3,6 @@
#include "Components/CameraComponent.h"
#include "Rendering/Execution/DirectionalShadowExecutionState.h"
#include "Rendering/Execution/Internal/CameraFrameGraph/Executor.h"
#include "Rendering/Passes/BuiltinDepthOnlyPass.h"
#include "Rendering/Passes/BuiltinObjectIdPass.h"
#include "Rendering/Passes/BuiltinShadowCasterPass.h"
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
#include "Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h"
#include "Rendering/Pipelines/ScriptableRenderPipelineHost.h"
#include "Rendering/RenderPipelineAsset.h"
@@ -23,14 +19,6 @@ std::shared_ptr<const RenderPipelineAsset> CreateDefaultPipelineAsset() {
return Pipelines::CreateManagedOrDefaultScriptableRenderPipelineAsset();
}
std::unique_ptr<RenderPass> CreateDefaultDepthOnlyPass() {
return std::make_unique<Passes::BuiltinDepthOnlyPass>();
}
std::unique_ptr<RenderPass> CreateDefaultShadowCasterPass() {
return std::make_unique<Passes::BuiltinShadowCasterPass>();
}
std::unique_ptr<RenderPipeline> CreatePipelineFromAsset(
const std::shared_ptr<const RenderPipelineAsset>& pipelineAsset) {
if (pipelineAsset != nullptr) {
@@ -85,40 +73,13 @@ CameraRenderer::CameraRenderer()
}
CameraRenderer::CameraRenderer(std::unique_ptr<RenderPipeline> pipeline)
: CameraRenderer(
std::move(pipeline),
std::make_unique<Passes::BuiltinObjectIdPass>(),
CreateDefaultDepthOnlyPass(),
CreateDefaultShadowCasterPass()) {
}
CameraRenderer::CameraRenderer(
std::unique_ptr<RenderPipeline> pipeline,
std::unique_ptr<RenderPass> objectIdPass,
std::unique_ptr<RenderPass> depthOnlyPass,
std::unique_ptr<RenderPass> shadowCasterPass)
: m_pipelineAsset(nullptr)
, m_objectIdPass(std::move(objectIdPass))
, m_depthOnlyPass(std::move(depthOnlyPass))
, m_shadowCasterPass(std::move(shadowCasterPass))
, m_directionalShadowRuntime(std::make_unique<DirectionalShadowRuntime>()) {
if (m_objectIdPass == nullptr) {
m_objectIdPass = std::make_unique<Passes::BuiltinObjectIdPass>();
}
if (m_depthOnlyPass == nullptr) {
m_depthOnlyPass = CreateDefaultDepthOnlyPass();
}
if (m_shadowCasterPass == nullptr) {
m_shadowCasterPass = CreateDefaultShadowCasterPass();
}
ResetPipeline(std::move(pipeline));
}
CameraRenderer::CameraRenderer(std::shared_ptr<const RenderPipelineAsset> pipelineAsset)
: m_pipelineAsset(std::move(pipelineAsset))
, m_objectIdPass(std::make_unique<Passes::BuiltinObjectIdPass>())
, m_depthOnlyPass(CreateDefaultDepthOnlyPass())
, m_shadowCasterPass(CreateDefaultShadowCasterPass())
, m_directionalShadowRuntime(std::make_unique<DirectionalShadowRuntime>()) {
SetPipelineAsset(m_pipelineAsset);
}
@@ -127,15 +88,6 @@ CameraRenderer::~CameraRenderer() {
if (m_pipeline) {
m_pipeline->Shutdown();
}
if (m_objectIdPass != nullptr) {
m_objectIdPass->Shutdown();
}
if (m_depthOnlyPass != nullptr) {
m_depthOnlyPass->Shutdown();
}
if (m_shadowCasterPass != nullptr) {
m_shadowCasterPass->Shutdown();
}
}
void CameraRenderer::SetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
@@ -148,39 +100,6 @@ void CameraRenderer::SetPipelineAsset(std::shared_ptr<const RenderPipelineAsset>
ResetPipeline(CreatePipelineFromAsset(m_pipelineAsset));
}
void CameraRenderer::SetObjectIdPass(std::unique_ptr<RenderPass> objectIdPass) {
if (m_objectIdPass != nullptr) {
m_objectIdPass->Shutdown();
}
m_objectIdPass = std::move(objectIdPass);
if (m_objectIdPass == nullptr) {
m_objectIdPass = std::make_unique<Passes::BuiltinObjectIdPass>();
}
}
void CameraRenderer::SetDepthOnlyPass(std::unique_ptr<RenderPass> depthOnlyPass) {
if (m_depthOnlyPass != nullptr) {
m_depthOnlyPass->Shutdown();
}
m_depthOnlyPass = std::move(depthOnlyPass);
if (m_depthOnlyPass == nullptr) {
m_depthOnlyPass = CreateDefaultDepthOnlyPass();
}
}
void CameraRenderer::SetShadowCasterPass(std::unique_ptr<RenderPass> shadowCasterPass) {
if (m_shadowCasterPass != nullptr) {
m_shadowCasterPass->Shutdown();
}
m_shadowCasterPass = std::move(shadowCasterPass);
if (m_shadowCasterPass == nullptr) {
m_shadowCasterPass = CreateDefaultShadowCasterPass();
}
}
void CameraRenderer::ResetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
if (m_pipeline != nullptr) {
m_pipeline->Shutdown();
@@ -227,10 +146,7 @@ bool CameraRenderer::ExecuteRenderPlan(
plan,
shadowState,
sceneData,
m_pipeline.get(),
m_objectIdPass.get(),
m_depthOnlyPass.get(),
m_shadowCasterPass.get());
m_pipeline.get());
}
bool CameraRenderer::Render(

View File

@@ -3,6 +3,7 @@
#include <XCEngine/Rendering/RenderPass.h>
#include <XCEngine/Rendering/RenderPipeline.h>
#include <array>
#include <memory>
namespace XCEngine {
@@ -46,14 +47,8 @@ private:
struct CameraFrameExecutionState {
RenderPipeline* pipeline = nullptr;
RenderPass* objectIdPass = nullptr;
RenderPass* depthOnlyPass = nullptr;
RenderPass* shadowCasterPass = nullptr;
std::unique_ptr<ScopedInitializedPassSequence> preScenePasses;
std::unique_ptr<ScopedInitializedPassSequence> postProcessPasses;
std::unique_ptr<ScopedInitializedPassSequence> finalOutputPasses;
std::unique_ptr<ScopedInitializedPassSequence> postScenePasses;
std::unique_ptr<ScopedInitializedPassSequence> overlayPasses;
std::array<std::unique_ptr<ScopedInitializedPassSequence>, kCameraFrameStageCount>
sequenceStates = {};
};
} // namespace Rendering

View File

@@ -16,19 +16,13 @@ bool ExecuteCameraFrameRenderGraphPlan(
const CameraFramePlan& plan,
const DirectionalShadowExecutionState& shadowState,
const RenderSceneData& sceneData,
RenderPipeline* pipeline,
RenderPass* objectIdPass,
RenderPass* depthOnlyPass,
RenderPass* shadowCasterPass) {
RenderPipeline* pipeline) {
RenderGraph graph = {};
RenderGraphBuilder graphBuilder(graph);
RenderGraphBlackboard blackboard = {};
CameraFrameExecutionState executionState = {};
executionState.pipeline = pipeline;
executionState.objectIdPass = objectIdPass;
executionState.depthOnlyPass = depthOnlyPass;
executionState.shadowCasterPass = shadowCasterPass;
bool stageExecutionSucceeded = true;
if (!RecordCameraFrameRenderGraphStages(

View File

@@ -13,10 +13,7 @@ bool ExecuteCameraFrameRenderGraphPlan(
const CameraFramePlan& plan,
const DirectionalShadowExecutionState& shadowState,
const RenderSceneData& sceneData,
RenderPipeline* pipeline,
RenderPass* objectIdPass,
RenderPass* depthOnlyPass,
RenderPass* shadowCasterPass);
RenderPipeline* pipeline);
} // namespace Rendering
} // namespace XCEngine

View File

@@ -28,20 +28,11 @@ bool EnsureInitializedPassSequence(
std::unique_ptr<ScopedInitializedPassSequence>* ResolveStageSequenceState(
CameraFrameStage stage,
CameraFrameExecutionState& executionState) {
switch (stage) {
case CameraFrameStage::PreScenePasses:
return &executionState.preScenePasses;
case CameraFrameStage::PostProcess:
return &executionState.postProcessPasses;
case CameraFrameStage::FinalOutput:
return &executionState.finalOutputPasses;
case CameraFrameStage::PostScenePasses:
return &executionState.postScenePasses;
case CameraFrameStage::OverlayPasses:
return &executionState.overlayPasses;
default:
if (!IsCameraFrameSequenceStage(stage)) {
return nullptr;
}
return &executionState.sequenceStates[GetCameraFrameStageIndex(stage)];
}
RenderPassGraphBeginCallback BuildSequenceBeginCallback(

View File

@@ -90,6 +90,33 @@ CameraFrameRenderGraphSourceBinding BuildCameraFrameStageGraphSourceBinding(
};
}
const ScenePassRenderRequest* ResolveCameraFrameStageScenePassRequest(
CameraFrameStage stage,
const CameraFrameRenderGraphStageContext& context) {
switch (GetCameraFrameStageRequestKind(stage)) {
case CameraFrameStageRequestKind::ShadowCaster:
return context.shadowState.shadowCasterRequest.IsRequested()
? &context.shadowState.shadowCasterRequest
: nullptr;
case CameraFrameStageRequestKind::DepthOnly:
return context.plan.request.depthOnly.IsRequested()
? &context.plan.request.depthOnly
: nullptr;
default:
return nullptr;
}
}
const ObjectIdRenderRequest* ResolveCameraFrameStageObjectIdRequest(
CameraFrameStage stage,
const CameraFrameRenderGraphStageContext& context) {
return GetCameraFrameStageRequestKind(stage) ==
CameraFrameStageRequestKind::ObjectId &&
context.plan.request.objectId.IsRequested()
? &context.plan.request.objectId
: nullptr;
}
RenderSceneData BuildCameraFrameScenePassRequestSceneData(
const ScenePassRenderRequest& request,
const RenderSurface& requestSurface,
@@ -130,15 +157,10 @@ RenderSceneData BuildCameraFrameStandaloneStageSceneData(
CameraFrameStage stage,
const CameraFrameRenderGraphStageContext& context,
const RenderSurface& stageSurface) {
if (stage == CameraFrameStage::ShadowCaster &&
context.shadowState.shadowCasterRequest.IsRequested()) {
return BuildCameraFrameScenePassRequestSceneData(
context.shadowState.shadowCasterRequest,
stageSurface,
context.sceneData);
}
if (const ScenePassRenderRequest* request = context.plan.GetScenePassRequest(stage);
if (const ScenePassRenderRequest* request =
ResolveCameraFrameStageScenePassRequest(
stage,
context);
request != nullptr) {
return BuildCameraFrameScenePassRequestSceneData(
*request,
@@ -152,16 +174,9 @@ RenderSceneData BuildCameraFrameStandaloneStageSceneData(
RenderPass* ResolveCameraFrameStandaloneStagePass(
CameraFrameStage stage,
CameraFrameExecutionState& executionState) {
switch (stage) {
case CameraFrameStage::ShadowCaster:
return executionState.shadowCasterPass;
case CameraFrameStage::DepthOnly:
return executionState.depthOnlyPass;
case CameraFrameStage::ObjectId:
return executionState.objectIdPass;
default:
return nullptr;
}
return executionState.pipeline != nullptr
? executionState.pipeline->GetCameraFrameStandalonePass(stage)
: nullptr;
}
bool InitializeCameraFrameStandaloneStagePass(
@@ -184,22 +199,29 @@ bool ExecuteCameraFrameRecordedStagePass(
const CameraFrameRenderGraphStageContext& context,
CameraFrameExecutionState& executionState,
const RenderPassContext& passContext) {
switch (stage) {
case CameraFrameStage::ShadowCaster:
if (IsCameraFrameScenePassRequestStage(stage)) {
const ScenePassRenderRequest* const request =
ResolveCameraFrameStageScenePassRequest(
stage,
context);
if (request == nullptr) {
return true;
}
RenderPass* const standaloneStagePass =
ResolveCameraFrameStandaloneStagePass(
stage,
executionState);
return ExecuteCameraFrameScenePassRequestStage(
executionState.shadowCasterPass,
context.shadowState.shadowCasterRequest,
standaloneStagePass,
*request,
context.plan.request.context,
context.sceneData,
passContext.surface);
case CameraFrameStage::DepthOnly:
return ExecuteCameraFrameScenePassRequestStage(
executionState.depthOnlyPass,
context.plan.request.depthOnly,
context.plan.request.context,
context.sceneData,
passContext.surface);
case CameraFrameStage::MainScene:
}
if (GetCameraFrameStageExecutionKind(stage) ==
CameraFrameStageExecutionKind::MainScenePipeline) {
return executionState.pipeline != nullptr &&
executionState.pipeline->Render(
FrameExecutionContext(
@@ -209,16 +231,26 @@ bool ExecuteCameraFrameRecordedStagePass(
passContext.sourceSurface,
passContext.sourceColorView,
passContext.sourceColorState));
case CameraFrameStage::ObjectId:
return !context.plan.request.objectId.IsRequested() ||
ExecuteCameraFrameStandaloneStagePass(
executionState.objectIdPass,
context.plan.request.context,
passContext.surface,
context.sceneData);
default:
return false;
}
if (GetCameraFrameStageRequestKind(stage) ==
CameraFrameStageRequestKind::ObjectId) {
if (ResolveCameraFrameStageObjectIdRequest(stage, context) == nullptr) {
return true;
}
RenderPass* const standaloneStagePass =
ResolveCameraFrameStandaloneStagePass(
stage,
executionState);
return ExecuteCameraFrameStandaloneStagePass(
standaloneStagePass,
context.plan.request.context,
passContext.surface,
context.sceneData);
}
return false;
}
RenderPassContext BuildCameraFrameStageGraphPassContext(

View File

@@ -23,6 +23,14 @@ struct CameraFrameStageFallbackSurfaceResolution {
CameraFrameRenderGraphSourceBinding BuildCameraFrameStageGraphSourceBinding(
const CameraFrameStageGraphBuildState& stageState);
const ScenePassRenderRequest* ResolveCameraFrameStageScenePassRequest(
CameraFrameStage stage,
const CameraFrameRenderGraphStageContext& context);
const ObjectIdRenderRequest* ResolveCameraFrameStageObjectIdRequest(
CameraFrameStage stage,
const CameraFrameRenderGraphStageContext& context);
RenderSceneData BuildCameraFrameScenePassRequestSceneData(
const ScenePassRenderRequest& request,
const RenderSurface& requestSurface,

View File

@@ -16,6 +16,7 @@ bool BuiltinForwardPipeline::Initialize(const RenderContext& context) {
void BuiltinForwardPipeline::Shutdown() {
m_forwardSceneFeatureHost.Shutdown();
DestroyPipelineResources();
ShutdownCameraFrameStandalonePasses();
}
bool BuiltinForwardPipeline::EnsureInitialized(const RenderContext& context) {

View File

@@ -1,5 +1,8 @@
#include "Rendering/Pipelines/ScriptableRenderPipelineHost.h"
#include "Rendering/Passes/BuiltinDepthOnlyPass.h"
#include "Rendering/Passes/BuiltinObjectIdPass.h"
#include "Rendering/Passes/BuiltinShadowCasterPass.h"
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
namespace XCEngine {
@@ -29,6 +32,18 @@ std::unique_ptr<RenderPipelineRenderer> CreateMainSceneRendererFromAsset(
return std::make_unique<BuiltinForwardPipeline>();
}
void InstallDefaultStandaloneStagePasses(RenderPipeline& pipeline) {
pipeline.SetCameraFrameStandalonePass(
CameraFrameStage::ObjectId,
std::make_unique<Passes::BuiltinObjectIdPass>());
pipeline.SetCameraFrameStandalonePass(
CameraFrameStage::DepthOnly,
std::make_unique<Passes::BuiltinDepthOnlyPass>());
pipeline.SetCameraFrameStandalonePass(
CameraFrameStage::ShadowCaster,
std::make_unique<Passes::BuiltinShadowCasterPass>());
}
} // namespace
ScriptableRenderPipelineHost::ScriptableRenderPipelineHost()
@@ -37,6 +52,7 @@ ScriptableRenderPipelineHost::ScriptableRenderPipelineHost()
ScriptableRenderPipelineHost::ScriptableRenderPipelineHost(
std::unique_ptr<RenderPipelineRenderer> mainSceneRenderer) {
InstallDefaultStandaloneStagePasses(*this);
ResetMainSceneRenderer(std::move(mainSceneRenderer));
}
@@ -46,6 +62,7 @@ ScriptableRenderPipelineHost::ScriptableRenderPipelineHost(
mainSceneRendererAsset != nullptr
? std::move(mainSceneRendererAsset)
: CreateDefaultMainSceneRendererAsset()) {
InstallDefaultStandaloneStagePasses(*this);
ResetMainSceneRenderer(
CreateMainSceneRendererFromAsset(m_mainSceneRendererAsset));
}
@@ -79,6 +96,7 @@ void ScriptableRenderPipelineHost::Shutdown() {
if (m_mainSceneRenderer != nullptr) {
m_mainSceneRenderer->Shutdown();
}
ShutdownCameraFrameStandalonePasses();
}
bool ScriptableRenderPipelineHost::SupportsMainSceneRenderGraph() const {