Refactor rendering frame execution contracts

This commit is contained in:
2026-04-13 22:16:04 +08:00
parent 48daaa1bd0
commit 712f99e723
30 changed files with 1398 additions and 247 deletions

View File

@@ -289,81 +289,88 @@ bool ExecuteFullscreenPassSequenceStage(
RenderPassContext BuildFrameStagePassContext(
CameraFrameStage stage,
const CameraRenderRequest& request,
const CameraFramePlan& plan,
const RenderSceneData& sceneData) {
const RenderSurface* outputSurface = request.GetOutputSurface(stage);
const RenderSurface* outputSurface = plan.GetOutputSurface(stage);
return {
request.context,
outputSurface != nullptr ? *outputSurface : request.surface,
plan.request.context,
outputSurface != nullptr ? *outputSurface : plan.request.surface,
sceneData,
request.GetSourceSurface(stage),
request.GetSourceColorView(stage),
request.GetSourceColorState(stage)
plan.GetSourceSurface(stage),
plan.GetSourceColorView(stage),
plan.GetSourceColorState(stage)
};
}
bool ExecuteFrameStage(
CameraFrameStage stage,
const CameraRenderRequest& request,
const CameraFramePlan& plan,
const ShadowCasterRenderRequest& resolvedShadowCaster,
const RenderSceneData& sceneData,
CameraFrameExecutionState& executionState) {
const RenderPassContext passContext = BuildFrameStagePassContext(stage, request, sceneData);
const RenderPassContext passContext = BuildFrameStagePassContext(stage, plan, sceneData);
switch (stage) {
case CameraFrameStage::PreScenePasses:
return ExecutePassSequenceStage(
executionState.preScenePasses,
request.GetPassSequence(stage),
request.context,
plan.GetPassSequence(stage),
plan.request.context,
passContext);
case CameraFrameStage::ShadowCaster:
return ExecuteScenePassRequest(
executionState.shadowCasterPass,
resolvedShadowCaster,
request.context,
plan.request.context,
sceneData);
case CameraFrameStage::DepthOnly:
return ExecuteScenePassRequest(
executionState.depthOnlyPass,
request.depthOnly,
request.context,
plan.request.depthOnly,
plan.request.context,
sceneData);
case CameraFrameStage::MainScene:
return executionState.pipeline != nullptr &&
executionState.pipeline->Render(request.context, passContext.surface, sceneData);
executionState.pipeline->Render(
FrameExecutionContext(
plan.request.context,
passContext.surface,
sceneData,
passContext.sourceSurface,
passContext.sourceColorView,
passContext.sourceColorState));
case CameraFrameStage::PostProcess:
return ExecuteFullscreenPassSequenceStage(
executionState.postProcessPasses,
request.GetPassSequence(stage),
request.context,
plan.GetPassSequence(stage),
plan.request.context,
passContext,
executionState.postProcessSurfaceCache);
case CameraFrameStage::FinalOutput:
return ExecuteFullscreenPassSequenceStage(
executionState.finalOutputPasses,
request.GetPassSequence(stage),
request.context,
plan.GetPassSequence(stage),
plan.request.context,
passContext,
executionState.finalOutputSurfaceCache);
case CameraFrameStage::ObjectId:
return !request.objectId.IsRequested() ||
return !plan.request.objectId.IsRequested() ||
ExecuteStandalonePass(
executionState.objectIdPass,
request.context,
request.objectId.surface,
plan.request.context,
plan.request.objectId.surface,
sceneData);
case CameraFrameStage::PostScenePasses:
return ExecutePassSequenceStage(
executionState.postScenePasses,
request.GetPassSequence(stage),
request.context,
plan.GetPassSequence(stage),
plan.request.context,
passContext);
case CameraFrameStage::OverlayPasses:
return ExecutePassSequenceStage(
executionState.overlayPasses,
request.GetPassSequence(stage),
request.context,
plan.GetPassSequence(stage),
plan.request.context,
passContext);
default:
return false;
@@ -396,27 +403,27 @@ RenderDirectionalShadowData BuildDirectionalShadowData(
return shadowData;
}
RenderEnvironmentData BuildEnvironmentData(const CameraRenderRequest& request) {
RenderEnvironmentData BuildEnvironmentData(const CameraFramePlan& plan) {
RenderEnvironmentData environment = {};
const RenderSurface& mainSceneSurface = request.GetMainSceneSurface();
if (request.camera == nullptr ||
const RenderSurface& mainSceneSurface = plan.GetMainSceneSurface();
if (plan.request.camera == nullptr ||
mainSceneSurface.GetDepthAttachment() == nullptr ||
!HasRenderClearFlag(request.clearFlags, RenderClearFlags::Color) ||
!request.camera->IsSkyboxEnabled() ||
request.camera->GetProjectionType() != Components::CameraProjectionType::Perspective) {
!HasRenderClearFlag(plan.request.clearFlags, RenderClearFlags::Color) ||
!plan.request.camera->IsSkyboxEnabled() ||
plan.request.camera->GetProjectionType() != Components::CameraProjectionType::Perspective) {
return environment;
}
if (const Resources::Material* skyboxMaterial = request.camera->GetSkyboxMaterial()) {
if (const Resources::Material* skyboxMaterial = plan.request.camera->GetSkyboxMaterial()) {
environment.mode = RenderEnvironmentMode::MaterialSkybox;
environment.materialSkybox.material = skyboxMaterial;
return environment;
}
environment.mode = RenderEnvironmentMode::ProceduralSkybox;
environment.skybox.topColor = request.camera->GetSkyboxTopColor();
environment.skybox.horizonColor = request.camera->GetSkyboxHorizonColor();
environment.skybox.bottomColor = request.camera->GetSkyboxBottomColor();
environment.skybox.topColor = plan.request.camera->GetSkyboxTopColor();
environment.skybox.horizonColor = plan.request.camera->GetSkyboxHorizonColor();
environment.skybox.bottomColor = plan.request.camera->GetSkyboxBottomColor();
return environment;
}
@@ -538,17 +545,17 @@ void CameraRenderer::ResetPipeline(std::unique_ptr<RenderPipeline> pipeline) {
}
bool CameraRenderer::ResolveShadowCasterRequest(
const CameraRenderRequest& request,
const CameraFramePlan& plan,
ShadowCasterRenderRequest& outResolvedShadowCaster,
RHI::RHIResourceView*& outShadowMapView) {
outResolvedShadowCaster = request.shadowCaster;
outResolvedShadowCaster = plan.shadowCaster;
outShadowMapView = nullptr;
if (outResolvedShadowCaster.IsRequested()) {
return outResolvedShadowCaster.IsValid();
}
if (!request.directionalShadow.IsValid()) {
if (!plan.directionalShadow.IsValid()) {
return true;
}
@@ -556,7 +563,7 @@ bool CameraRenderer::ResolveShadowCasterRequest(
m_directionalShadowSurface = std::make_unique<DirectionalShadowSurfaceCache>();
}
if (!m_directionalShadowSurface->EnsureSurface(request.context, request.directionalShadow)) {
if (!m_directionalShadowSurface->EnsureSurface(plan.request.context, plan.directionalShadow)) {
return false;
}
@@ -564,44 +571,44 @@ bool CameraRenderer::ResolveShadowCasterRequest(
outResolvedShadowCaster.clearFlags = RenderClearFlags::Depth;
if (!outResolvedShadowCaster.hasCameraDataOverride) {
outResolvedShadowCaster.hasCameraDataOverride = true;
outResolvedShadowCaster.cameraDataOverride = request.directionalShadow.cameraData;
outResolvedShadowCaster.cameraDataOverride = plan.directionalShadow.cameraData;
}
outShadowMapView = m_directionalShadowSurface->GetDepthShaderView();
return true;
}
bool CameraRenderer::BuildSceneDataForRequest(
const CameraRenderRequest& request,
bool CameraRenderer::BuildSceneDataForPlan(
const CameraFramePlan& plan,
RHI::RHIResourceView* shadowMapView,
RenderSceneData& outSceneData) {
const RenderSurface& mainSceneSurface = request.GetMainSceneSurface();
const RenderSurface& mainSceneSurface = plan.GetMainSceneSurface();
outSceneData = m_sceneExtractor.ExtractForCamera(
*request.scene,
*request.camera,
*plan.request.scene,
*plan.request.camera,
mainSceneSurface.GetRenderAreaWidth(),
mainSceneSurface.GetRenderAreaHeight());
if (!outSceneData.HasCamera()) {
return false;
}
if (request.directionalShadow.IsValid()) {
if (plan.directionalShadow.IsValid()) {
outSceneData.lighting.mainDirectionalShadow =
BuildDirectionalShadowData(request.directionalShadow, shadowMapView);
BuildDirectionalShadowData(plan.directionalShadow, shadowMapView);
}
outSceneData.globalShaderKeywords = BuildSceneGlobalShaderKeywords(outSceneData);
outSceneData.cameraData.clearFlags = request.clearFlags;
outSceneData.environment = BuildEnvironmentData(request);
if (request.hasClearColorOverride) {
outSceneData.cameraData.clearColor = request.clearColorOverride;
outSceneData.cameraData.clearFlags = plan.request.clearFlags;
outSceneData.environment = BuildEnvironmentData(plan);
if (plan.request.hasClearColorOverride) {
outSceneData.cameraData.clearColor = plan.request.clearColorOverride;
}
return true;
}
bool CameraRenderer::ExecuteRenderPlan(
const CameraRenderRequest& request,
const CameraFramePlan& plan,
const ShadowCasterRenderRequest& resolvedShadowCaster,
const RenderSceneData& sceneData) {
CameraFrameExecutionState executionState = {};
@@ -613,14 +620,14 @@ bool CameraRenderer::ExecuteRenderPlan(
executionState.finalOutputSurfaceCache = m_finalOutputSurfaceCache.get();
for (const CameraFrameStageInfo& stageInfo : kOrderedCameraFrameStages) {
if (!request.HasFrameStage(stageInfo.stage) &&
if (!plan.HasFrameStage(stageInfo.stage) &&
stageInfo.stage != CameraFrameStage::MainScene) {
continue;
}
if (!ExecuteFrameStage(
stageInfo.stage,
request,
plan,
resolvedShadowCaster,
sceneData,
executionState)) {
@@ -633,14 +640,19 @@ bool CameraRenderer::ExecuteRenderPlan(
bool CameraRenderer::Render(
const CameraRenderRequest& request) {
if (!request.IsValid() || m_pipeline == nullptr) {
return Render(CameraFramePlan::FromRequest(request));
}
bool CameraRenderer::Render(
const CameraFramePlan& plan) {
if (!plan.IsValid() || m_pipeline == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"CameraRenderer::Render failed: request invalid or pipeline missing");
"CameraRenderer::Render failed: plan invalid or pipeline missing");
return false;
}
const RenderSurface& mainSceneSurface = request.GetMainSceneSurface();
const RenderSurface& mainSceneSurface = plan.GetMainSceneSurface();
if (mainSceneSurface.GetRenderAreaWidth() == 0 ||
mainSceneSurface.GetRenderAreaHeight() == 0) {
Debug::Logger::Get().Error(
@@ -648,29 +660,29 @@ bool CameraRenderer::Render(
"CameraRenderer::Render failed: main scene surface render area is empty");
return false;
}
if (request.depthOnly.IsRequested() &&
!request.depthOnly.IsValid()) {
if (plan.request.depthOnly.IsRequested() &&
!plan.request.depthOnly.IsValid()) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"CameraRenderer::Render failed: depth-only request invalid");
return false;
}
if (request.postProcess.IsRequested() &&
!request.postProcess.IsValid()) {
if (plan.postProcess.IsRequested() &&
!plan.postProcess.IsValid()) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"CameraRenderer::Render failed: post-process request invalid");
return false;
}
if (request.finalOutput.IsRequested() &&
!request.finalOutput.IsValid()) {
if (plan.finalOutput.IsRequested() &&
!plan.finalOutput.IsValid()) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"CameraRenderer::Render failed: final-output request invalid");
return false;
}
if (request.objectId.IsRequested() &&
!request.objectId.IsValid()) {
if (plan.request.objectId.IsRequested() &&
!plan.request.objectId.IsValid()) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"CameraRenderer::Render failed: object-id request invalid");
@@ -679,7 +691,7 @@ bool CameraRenderer::Render(
ShadowCasterRenderRequest resolvedShadowCaster = {};
RHI::RHIResourceView* shadowMapView = nullptr;
if (!ResolveShadowCasterRequest(request, resolvedShadowCaster, shadowMapView)) {
if (!ResolveShadowCasterRequest(plan, resolvedShadowCaster, shadowMapView)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"CameraRenderer::Render failed: ResolveShadowCasterRequest returned false");
@@ -687,14 +699,14 @@ bool CameraRenderer::Render(
}
RenderSceneData sceneData = {};
if (!BuildSceneDataForRequest(request, shadowMapView, sceneData)) {
if (!BuildSceneDataForPlan(plan, shadowMapView, sceneData)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"CameraRenderer::Render failed: BuildSceneDataForRequest returned false");
"CameraRenderer::Render failed: BuildSceneDataForPlan returned false");
return false;
}
if (!ExecuteRenderPlan(request, resolvedShadowCaster, sceneData)) {
if (!ExecuteRenderPlan(plan, resolvedShadowCaster, sceneData)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"CameraRenderer::Render failed: ExecuteRenderPlan returned false");

View File

@@ -13,6 +13,14 @@ namespace Rendering {
namespace {
bool CompareCameraFramePlans(
const CameraFramePlan& lhs,
const CameraFramePlan& rhs) {
return SceneRenderRequestUtils::CompareCameraRenderRequests(
lhs.request,
rhs.request);
}
RenderSurface ConfigureFullscreenStageSurface(
const FullscreenPassSurfaceCache::SurfaceEntry& entry,
const RenderSurface& templateSurface,
@@ -94,44 +102,72 @@ std::vector<CameraRenderRequest> SceneRenderer::BuildRenderRequests(
Components::CameraComponent* overrideCamera,
const RenderContext& context,
const RenderSurface& surface) {
std::vector<CameraRenderRequest> requests =
m_requestPlanner.BuildRequests(scene, overrideCamera, context, surface);
ResolveCameraFinalColorPolicies(requests);
AttachFullscreenStageRequests(context, requests);
const std::vector<CameraFramePlan> plans =
BuildFramePlans(scene, overrideCamera, context, surface);
std::vector<CameraRenderRequest> requests = {};
requests.reserve(plans.size());
for (const CameraFramePlan& plan : plans) {
requests.push_back(BuildLegacyCameraRenderRequest(plan));
}
return requests;
}
std::vector<CameraFramePlan> SceneRenderer::BuildFramePlans(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera,
const RenderContext& context,
const RenderSurface& surface) {
const std::vector<CameraRenderRequest> requests =
m_requestPlanner.BuildRequests(scene, overrideCamera, context, surface);
std::vector<CameraFramePlan> plans = CreateFramePlansFromRequests(requests);
ResolveCameraFinalColorPolicies(plans);
AttachFullscreenStageRequests(context, plans);
return plans;
}
bool SceneRenderer::Render(const CameraRenderRequest& request) {
return m_cameraRenderer.Render(request);
return Render(CameraFramePlan::FromRequest(request));
}
bool SceneRenderer::Render(const std::vector<CameraRenderRequest>& requests) {
if (requests.empty()) {
std::vector<CameraFramePlan> plans = CreateFramePlansFromRequests(requests);
return Render(plans);
}
bool SceneRenderer::Render(const CameraFramePlan& plan) {
return m_cameraRenderer.Render(plan);
}
bool SceneRenderer::Render(const std::vector<CameraFramePlan>& plans) {
if (plans.empty()) {
return false;
}
for (const CameraRenderRequest& request : requests) {
if (!request.IsValid()) {
for (const CameraFramePlan& plan : plans) {
if (!plan.IsValid()) {
return false;
}
}
std::vector<CameraRenderRequest> sortedRequests = requests;
SceneRenderRequestUtils::SortCameraRenderRequests(sortedRequests);
std::vector<CameraFramePlan> sortedPlans = plans;
std::stable_sort(
sortedPlans.begin(),
sortedPlans.end(),
CompareCameraFramePlans);
bool rendered = false;
for (const CameraRenderRequest& request : sortedRequests) {
if (!m_cameraRenderer.Render(request)) {
for (const CameraFramePlan& plan : sortedPlans) {
if (!m_cameraRenderer.Render(plan)) {
return false;
}
UpdateTrackedFullscreenSurfaceState(
m_ownedFullscreenStageSurfaces,
&request.GetMainSceneSurface());
if (request.postProcess.IsRequested()) {
&plan.GetMainSceneSurface());
if (plan.postProcess.IsRequested()) {
UpdateTrackedFullscreenSurfaceState(
m_ownedFullscreenStageSurfaces,
&request.postProcess.destinationSurface);
&plan.postProcess.destinationSurface);
}
rendered = true;
@@ -145,7 +181,18 @@ bool SceneRenderer::Render(
Components::CameraComponent* overrideCamera,
const RenderContext& context,
const RenderSurface& surface) {
return Render(BuildRenderRequests(scene, overrideCamera, context, surface));
return Render(BuildFramePlans(scene, overrideCamera, context, surface));
}
std::vector<CameraFramePlan> SceneRenderer::CreateFramePlansFromRequests(
const std::vector<CameraRenderRequest>& requests) const {
std::vector<CameraFramePlan> plans = {};
plans.reserve(requests.size());
for (const CameraRenderRequest& request : requests) {
plans.push_back(CameraFramePlan::FromRequest(request));
}
return plans;
}
void SceneRenderer::PrepareOwnedFullscreenStageState(size_t requestCount) {
@@ -166,39 +213,39 @@ void SceneRenderer::PrepareOwnedFullscreenStageState(size_t requestCount) {
}
void SceneRenderer::ResolveCameraFinalColorPolicies(
std::vector<CameraRenderRequest>& requests) const {
std::vector<CameraFramePlan>& plans) const {
const RenderPipelineAsset* pipelineAsset = GetPipelineAsset();
const FinalColorSettings pipelineDefaults =
pipelineAsset != nullptr ? pipelineAsset->GetDefaultFinalColorSettings() : FinalColorSettings();
for (CameraRenderRequest& request : requests) {
if (request.camera == nullptr) {
for (CameraFramePlan& plan : plans) {
if (plan.request.camera == nullptr) {
continue;
}
request.finalColorPolicy = ResolveFinalColorPolicy(
plan.finalColorPolicy = ResolveFinalColorPolicy(
pipelineDefaults,
&request.camera->GetFinalColorOverrides());
&plan.request.camera->GetFinalColorOverrides());
}
}
void SceneRenderer::AttachFullscreenStageRequests(
const RenderContext& context,
std::vector<CameraRenderRequest>& requests) {
PrepareOwnedFullscreenStageState(requests.size());
std::vector<CameraFramePlan>& plans) {
PrepareOwnedFullscreenStageState(plans.size());
for (size_t index = 0; index < requests.size(); ++index) {
CameraRenderRequest& request = requests[index];
if (request.camera == nullptr ||
request.context.device == nullptr ||
!HasValidColorTarget(request.surface)) {
for (size_t index = 0; index < plans.size(); ++index) {
CameraFramePlan& plan = plans[index];
if (plan.request.camera == nullptr ||
plan.request.context.device == nullptr ||
!HasValidColorTarget(plan.request.surface)) {
continue;
}
std::unique_ptr<RenderPassSequence> postProcessSequence =
BuildCameraPostProcessPassSequence(request.camera->GetPostProcessPasses());
BuildCameraPostProcessPassSequence(plan.request.camera->GetPostProcessPasses());
std::unique_ptr<RenderPassSequence> finalOutputSequence =
BuildFinalColorPassSequence(request.finalColorPolicy);
BuildFinalColorPassSequence(plan.finalColorPolicy);
const bool hasPostProcess =
postProcessSequence != nullptr && postProcessSequence->GetPassCount() > 0u;
@@ -208,14 +255,14 @@ void SceneRenderer::AttachFullscreenStageRequests(
continue;
}
if (request.surface.GetSampleCount() > 1u) {
if (plan.request.surface.GetSampleCount() > 1u) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"SceneRenderer fullscreen post-process/final-output chain currently requires a single-sample main scene surface");
continue;
}
const std::vector<RHI::RHIResourceView*>& colorAttachments = request.surface.GetColorAttachments();
const std::vector<RHI::RHIResourceView*>& colorAttachments = plan.request.surface.GetColorAttachments();
const RHI::Format colorFormat = colorAttachments[0]->GetFormat();
if (colorFormat == RHI::Format::Unknown) {
continue;
@@ -226,8 +273,8 @@ void SceneRenderer::AttachFullscreenStageRequests(
if (surfaceCache == nullptr ||
!surfaceCache->EnsureSurfaces(
context,
request.surface.GetWidth(),
request.surface.GetHeight(),
plan.request.surface.GetWidth(),
plan.request.surface.GetHeight(),
colorFormat,
fullscreenSurfaceCount)) {
continue;
@@ -246,30 +293,30 @@ void SceneRenderer::AttachFullscreenStageRequests(
}
if (hasPostProcess) {
request.postProcess.sourceSurface =
ConfigureFullscreenStageSurface(*sceneColorEntry, request.surface, true);
request.postProcess.sourceColorView = sceneColorEntry->shaderResourceView;
request.postProcess.sourceColorState = request.postProcess.sourceSurface.GetColorStateAfter();
request.postProcess.destinationSurface =
plan.postProcess.sourceSurface =
ConfigureFullscreenStageSurface(*sceneColorEntry, plan.request.surface, true);
plan.postProcess.sourceColorView = sceneColorEntry->shaderResourceView;
plan.postProcess.sourceColorState = plan.postProcess.sourceSurface.GetColorStateAfter();
plan.postProcess.destinationSurface =
hasFinalOutput
? ConfigureFullscreenStageSurface(*postProcessOutputEntry, request.surface, false)
: request.surface;
? ConfigureFullscreenStageSurface(*postProcessOutputEntry, plan.request.surface, false)
: plan.request.surface;
m_ownedPostProcessSequences[index] = std::move(postProcessSequence);
request.postProcess.passes = m_ownedPostProcessSequences[index].get();
plan.postProcess.passes = m_ownedPostProcessSequences[index].get();
}
if (hasFinalOutput) {
const FullscreenPassSurfaceCache::SurfaceEntry* finalOutputSourceEntry =
hasPostProcess ? postProcessOutputEntry : sceneColorEntry;
request.finalOutput.sourceSurface =
plan.finalOutput.sourceSurface =
hasPostProcess
? request.postProcess.destinationSurface
: ConfigureFullscreenStageSurface(*sceneColorEntry, request.surface, true);
request.finalOutput.sourceColorView = finalOutputSourceEntry->shaderResourceView;
request.finalOutput.sourceColorState = request.finalOutput.sourceSurface.GetColorStateAfter();
request.finalOutput.destinationSurface = request.surface;
? plan.postProcess.destinationSurface
: ConfigureFullscreenStageSurface(*sceneColorEntry, plan.request.surface, true);
plan.finalOutput.sourceColorView = finalOutputSourceEntry->shaderResourceView;
plan.finalOutput.sourceColorState = plan.finalOutput.sourceSurface.GetColorStateAfter();
plan.finalOutput.destinationSurface = plan.request.surface;
m_ownedFinalOutputSequences[index] = std::move(finalOutputSequence);
request.finalOutput.passes = m_ownedFinalOutputSequences[index].get();
plan.finalOutput.passes = m_ownedFinalOutputSequences[index].get();
}
}
}