Align rendering orchestration with camera stack plans
This commit is contained in:
@@ -507,6 +507,7 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Builtin/BuiltinPassLayoutUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/CameraFramePlan.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/CameraStackFramePlan.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/CameraFrameStage.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/CameraRenderer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/DirectionalShadowExecutionState.h
|
||||
@@ -575,6 +576,7 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/CameraFramePlan.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/CameraStackFramePlan.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/CameraRenderer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/RenderPipelineHost.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/SceneRenderSequence.cpp
|
||||
|
||||
@@ -62,6 +62,9 @@ Unity 兼容的公开命名、对象所有权和扩展点。
|
||||
具体后端头。
|
||||
- 新增 camera-stack、renderer-index、shadow、depth、post-process 或 final output 行为时,不要绕过
|
||||
`CameraFramePlanBuilder` 或 `SceneRenderRequestPlanner`。
|
||||
- 修改 camera stack 行为时,要保持 `CameraRenderRequest.cameraStackId`、`CameraStackFramePlan`、
|
||||
`RenderPipelineHost::BuildStackFramePlans` 和 `SceneRenderer::BuildStackFramePlans` 同步。
|
||||
`CameraFramePlan` 是每个 camera 的叶子计划;`CameraStackFramePlan` 是顶层渲染生命周期根。
|
||||
- 不要在 native 长生命周期对象里保存 raw managed object pointers 或 managed context handles。
|
||||
需要持久化时,通过现有 external managed object handle 系统 retain。
|
||||
- Managed URP public APIs 不要依赖 `CameraFrameGraph` 或 `NativeSceneRecorder` 这类 native
|
||||
@@ -97,8 +100,9 @@ Unity 兼容的公开命名、对象所有权和扩展点。
|
||||
built-in forward pipeline policy,而不是作为 URP-declared work 的 backend executor。
|
||||
- 目标所有权模型是:URP 决定渲染什么、何时渲染、哪些 renderer lists/passes/features 激活,以及
|
||||
哪些 stages 存在;native 只执行这些声明,不夹带 built-in pipeline policy。
|
||||
- `CameraFramePlanBuilder` 现在只负责从 request 构造 `CameraFramePlan` 并委托所选
|
||||
`RenderPipelineAsset::ConfigureCameraFramePlan`。它不再在 asset hook 之后追加通用 legacy
|
||||
- `CameraFramePlanBuilder` 现在负责从 request 构造 per-camera `CameraFramePlan`,再按
|
||||
`CameraRenderRequest.cameraStackId` 收口为 `CameraStackFramePlan`。它委托所选
|
||||
`RenderPipelineAsset::ConfigureCameraFramePlan` 配置叶子计划,不再在 asset hook 之后追加通用 legacy
|
||||
fullscreen stage heuristic;默认 native fullscreen/final-output 行为应留在 native asset policy 内部。
|
||||
- Hidden fallback 很危险。如果 managed URP stage 声明支持但无法 record,失败必须可见。不要静默用
|
||||
default built-in path 画同一个 stage,然后称之为 URP。
|
||||
@@ -132,10 +136,12 @@ Native renderer 现在是 camera frame planner 加 RenderGraph executor,manage
|
||||
叠在 native scene draw backend 之上。
|
||||
|
||||
- `SceneRenderer` 是 scene-level convenience entry point。它的公开管线选择根是 `RenderPipelineAsset`;
|
||||
它通过 `SceneRenderRequestPlanner` 收集 camera requests,通过 `RenderPipelineHost` 构建 frame plans,
|
||||
然后渲染排序后的 camera plans。
|
||||
它通过 `SceneRenderRequestPlanner` 收集 camera requests,通过 `RenderPipelineHost` 构建
|
||||
`CameraStackFramePlan`,然后以 camera stack 为根渲染。旧的 `BuildFramePlans`/flat-plan `Render`
|
||||
API 只是兼容包装。
|
||||
- `RenderPipelineHost` 拥有一个 `CameraFramePlanBuilder` 和一个 `CameraRenderer`。它是从
|
||||
`CameraRenderRequest` 到 `CameraFramePlan` 的常规 native bridge,并沿用同一个 asset selection root。
|
||||
`CameraRenderRequest` 到 `CameraStackFramePlan` 的常规 native bridge,并沿用同一个 asset selection root。
|
||||
Stack 渲染完成后由所选 `RenderPipeline` 接收 `FinishCameraStackRendering`,用于 URP stack 生命周期收尾。
|
||||
- `CameraRenderer` 拥有 asset 创建出的所选 `RenderPipeline`,提取 `RenderSceneData`,解析 directional
|
||||
shadow execution,把全部 frame stages 记录进 native `RenderGraph`,编译 graph 并执行。
|
||||
- `RenderPipelineAsset` 是 native asset contract。它创建 pipeline,配置 pipeline 实例,配置 request
|
||||
@@ -265,6 +271,9 @@ package。
|
||||
- `RendererFramePlan` 不得只是 pass 对象引用列表。`ScriptableRenderPass.CreateFramePlanSnapshot` 是冻结
|
||||
pass 状态的边界;新增 mutable pass state 时必须确认它能被 frame-plan snapshot 捕获,避免后续 camera
|
||||
planning 改写前一个 camera 的 recording。
|
||||
- `OnFinishCameraStackRendering` 是 camera-stack 生命周期回调,不是 per-stage 或 per-camera-frame 回调。
|
||||
`ScriptableRenderer` 可以在 `RecordRendererFromFramePlan` 时登记待收尾的 `RendererFramePlan`,但只能由
|
||||
native `CameraStackFramePlan` 渲染完成后的 `FinishCameraStackRendering` 触发实际回调。
|
||||
- URP features 的 `AddRenderPasses` 是 per-camera 声明点,不是 per-stage 回调。不要通过
|
||||
stage 数据分阶段重复 enqueue;`AddRenderPasses`/`SetupRenderPasses` 必须使用 `RendererPassQueueData`,
|
||||
而不是 recording 阶段的 `RenderingData`。pass 所属 stage 应由 `RenderPassEvent -> RendererBlock`
|
||||
@@ -377,6 +386,8 @@ Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。
|
||||
authoring 还未公开。
|
||||
- `UniversalPostProcessBlock` 仍保留 post-process source promotion helper;实际 post-process stage 由
|
||||
active pass queue 中的 features/passes 声明。
|
||||
- 当前 camera stack 归属仍来自 `SceneRenderRequestPlanner` 对 base/overlay 请求流的 `cameraStackId`
|
||||
分组;引擎相机数据模型还没有 Unity `cameraStack` 那种显式 per-base overlay 列表。
|
||||
- Render-pipeline selection 已切到 `AssetRef` 作为根身份,并且 managed SRP descriptor 已保存
|
||||
ScriptableObject graph snapshot;完整 editor/importer `.asset` 持久化和 sub-asset authoring 仍未完成。
|
||||
- 当前 shadow support 是单个 main directional shadow path,没有 cascades。
|
||||
@@ -411,6 +422,10 @@ Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。
|
||||
- URP stage planning 已收口到 renderer active pass queue 派生的 per-`framePlanId` `RendererFramePlan` 和
|
||||
stage manifest。Stage support 和 stage recording 现在消费 planning 阶段保存的同一份冻结 plan,
|
||||
关闭了 feature planning hook、support probe、recording 和各 stage 分别重建 pass queue 的重复事实源。
|
||||
- 顶层 native orchestration 已从 flat camera frame list 收口为 `CameraStackFramePlan`。`SceneRenderer`
|
||||
和 `RenderPipelineHost` 的真实渲染入口以 camera stack 为根;flat `CameraFramePlan` APIs 保留为兼容包装。
|
||||
- URP `OnFinishCameraStackRendering` 已改为 stack-end 语义:recording 阶段只登记待收尾 pass,native
|
||||
`CameraStackFramePlan` 渲染完成后再通过 pipeline/stage-recorder bridge 触发。
|
||||
- `CameraFramePlanBuilder` 已移除对 fullscreen stages 的通用尾部自动补齐;selected
|
||||
`RenderPipelineAsset` 现在是 native plan policy 的唯一入口,managed URP path 不再保留第二个
|
||||
fullscreen stage planner。
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/Execution/CameraFramePlan.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
struct CameraStackFramePlan {
|
||||
uint64_t stackPlanId = 0u;
|
||||
uint64_t cameraStackId = 0u;
|
||||
std::vector<CameraFramePlan> cameraPlans = {};
|
||||
|
||||
static CameraStackFramePlan FromCameraPlans(
|
||||
uint64_t cameraStackId,
|
||||
std::vector<CameraFramePlan> cameraPlans);
|
||||
|
||||
bool IsValid() const;
|
||||
bool HasBaseCamera() const;
|
||||
bool IsOverlayOnly() const;
|
||||
const CameraFramePlan* GetBaseCameraPlan() const;
|
||||
const CameraFramePlan* GetSortKeyPlan() const;
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/Execution/CameraStackFramePlan.h>
|
||||
#include <XCEngine/Rendering/Execution/CameraRenderer.h>
|
||||
|
||||
#include <memory>
|
||||
@@ -22,9 +23,13 @@ public:
|
||||
|
||||
std::vector<CameraFramePlan> BuildFramePlans(
|
||||
const std::vector<CameraRenderRequest>& requests);
|
||||
std::vector<CameraStackFramePlan> BuildStackFramePlans(
|
||||
const std::vector<CameraRenderRequest>& requests);
|
||||
|
||||
bool Render(const CameraFramePlan& plan);
|
||||
bool Render(const std::vector<CameraFramePlan>& plans);
|
||||
bool Render(const CameraStackFramePlan& stackPlan);
|
||||
bool Render(const std::vector<CameraStackFramePlan>& stackPlans);
|
||||
|
||||
private:
|
||||
CameraRenderer m_cameraRenderer;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/Execution/CameraStackFramePlan.h>
|
||||
#include <XCEngine/Rendering/Execution/RenderPipelineHost.h>
|
||||
#include <XCEngine/Rendering/Planning/SceneRenderRequestPlanner.h>
|
||||
|
||||
@@ -28,9 +29,16 @@ public:
|
||||
Components::CameraComponent* overrideCamera,
|
||||
const RenderContext& context,
|
||||
const RenderSurface& surface);
|
||||
std::vector<CameraStackFramePlan> BuildStackFramePlans(
|
||||
const Components::Scene& scene,
|
||||
Components::CameraComponent* overrideCamera,
|
||||
const RenderContext& context,
|
||||
const RenderSurface& surface);
|
||||
|
||||
bool Render(const CameraFramePlan& plan);
|
||||
bool Render(const std::vector<CameraFramePlan>& plans);
|
||||
bool Render(const CameraStackFramePlan& stackPlan);
|
||||
bool Render(const std::vector<CameraStackFramePlan>& stackPlans);
|
||||
bool Render(
|
||||
const Components::Scene& scene,
|
||||
Components::CameraComponent* overrideCamera,
|
||||
|
||||
@@ -60,6 +60,8 @@ public:
|
||||
const RenderContext& context,
|
||||
const RenderSurface& surface,
|
||||
const RenderSceneData& sceneData) override;
|
||||
void FinishCameraStackRendering(
|
||||
const CameraStackFramePlan& stackPlan) override;
|
||||
void ConfigureRenderSceneData(
|
||||
const CameraFramePlan& plan,
|
||||
RenderSceneData& sceneData) const override;
|
||||
|
||||
@@ -27,6 +27,7 @@ struct CameraRenderRequest {
|
||||
FinalOutputRenderRequest finalOutput;
|
||||
ResolvedFinalColorPolicy finalColorPolicy = {};
|
||||
ObjectIdRenderRequest objectId;
|
||||
uint64_t cameraStackId = 0u;
|
||||
float cameraDepth = 0.0f;
|
||||
uint8_t cameraStackOrder = 0;
|
||||
int32_t rendererIndex = -1;
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Rendering {
|
||||
|
||||
class RenderGraphBuilder;
|
||||
struct CameraFramePlan;
|
||||
struct CameraStackFramePlan;
|
||||
struct DirectionalShadowExecutionState;
|
||||
struct DirectionalShadowSurfaceAllocation;
|
||||
|
||||
@@ -91,6 +92,8 @@ public:
|
||||
const RenderPipelineStageRenderGraphContext&) {
|
||||
return false;
|
||||
}
|
||||
virtual void FinishCameraStackRendering(
|
||||
const CameraStackFramePlan&) {}
|
||||
};
|
||||
|
||||
class RenderPipelineBackend {
|
||||
@@ -142,6 +145,8 @@ public:
|
||||
const CameraFramePlan& plan,
|
||||
const DirectionalShadowSurfaceAllocation& shadowAllocation,
|
||||
DirectionalShadowExecutionState& shadowState) const;
|
||||
virtual void FinishCameraStackRendering(
|
||||
const CameraStackFramePlan&) {}
|
||||
|
||||
void SetCameraFrameStandalonePass(
|
||||
CameraFrameStage stage,
|
||||
|
||||
89
engine/src/Rendering/Execution/CameraStackFramePlan.cpp
Normal file
89
engine/src/Rendering/Execution/CameraStackFramePlan.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include <XCEngine/Rendering/Execution/CameraStackFramePlan.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
namespace {
|
||||
|
||||
uint64_t AllocateCameraStackFramePlanId() {
|
||||
static std::atomic<uint64_t> s_nextStackPlanId{ 1u };
|
||||
uint64_t stackPlanId =
|
||||
s_nextStackPlanId.fetch_add(
|
||||
1u,
|
||||
std::memory_order_relaxed);
|
||||
if (stackPlanId == 0u) {
|
||||
stackPlanId =
|
||||
s_nextStackPlanId.fetch_add(
|
||||
1u,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
return stackPlanId;
|
||||
}
|
||||
|
||||
bool IsBaseCameraPlan(const CameraFramePlan& plan) {
|
||||
return plan.request.cameraStackOrder == 0u;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CameraStackFramePlan CameraStackFramePlan::FromCameraPlans(
|
||||
uint64_t cameraStackId,
|
||||
std::vector<CameraFramePlan> cameraPlans) {
|
||||
CameraStackFramePlan stackPlan = {};
|
||||
stackPlan.stackPlanId = AllocateCameraStackFramePlanId();
|
||||
stackPlan.cameraStackId = cameraStackId;
|
||||
stackPlan.cameraPlans = std::move(cameraPlans);
|
||||
return stackPlan;
|
||||
}
|
||||
|
||||
bool CameraStackFramePlan::IsValid() const {
|
||||
if (stackPlanId == 0u ||
|
||||
cameraPlans.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const CameraFramePlan& plan : cameraPlans) {
|
||||
if (!plan.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CameraStackFramePlan::HasBaseCamera() const {
|
||||
return GetBaseCameraPlan() != nullptr;
|
||||
}
|
||||
|
||||
bool CameraStackFramePlan::IsOverlayOnly() const {
|
||||
return !cameraPlans.empty() &&
|
||||
!HasBaseCamera();
|
||||
}
|
||||
|
||||
const CameraFramePlan* CameraStackFramePlan::GetBaseCameraPlan() const {
|
||||
for (const CameraFramePlan& plan : cameraPlans) {
|
||||
if (IsBaseCameraPlan(plan)) {
|
||||
return &plan;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const CameraFramePlan* CameraStackFramePlan::GetSortKeyPlan() const {
|
||||
if (const CameraFramePlan* const basePlan = GetBaseCameraPlan();
|
||||
basePlan != nullptr) {
|
||||
return basePlan;
|
||||
}
|
||||
|
||||
return cameraPlans.empty()
|
||||
? nullptr
|
||||
: &cameraPlans.front();
|
||||
}
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -3,6 +3,9 @@
|
||||
#include "Rendering/Planning/CameraFramePlanBuilder.h"
|
||||
#include "Rendering/Planning/SceneRenderRequestUtils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
@@ -16,6 +19,23 @@ bool CompareCameraFramePlans(
|
||||
rhs.request);
|
||||
}
|
||||
|
||||
bool CompareCameraStackFramePlans(
|
||||
const CameraStackFramePlan& lhs,
|
||||
const CameraStackFramePlan& rhs) {
|
||||
const CameraFramePlan* const lhsSortKeyPlan =
|
||||
lhs.GetSortKeyPlan();
|
||||
const CameraFramePlan* const rhsSortKeyPlan =
|
||||
rhs.GetSortKeyPlan();
|
||||
if (lhsSortKeyPlan == nullptr ||
|
||||
rhsSortKeyPlan == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CompareCameraFramePlans(
|
||||
*lhsSortKeyPlan,
|
||||
*rhsSortKeyPlan);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
RenderPipelineHost::RenderPipelineHost()
|
||||
@@ -42,8 +62,22 @@ std::vector<CameraFramePlan> RenderPipelineHost::BuildFramePlans(
|
||||
: std::vector<CameraFramePlan>();
|
||||
}
|
||||
|
||||
std::vector<CameraStackFramePlan> RenderPipelineHost::BuildStackFramePlans(
|
||||
const std::vector<CameraRenderRequest>& requests) {
|
||||
return m_framePlanBuilder != nullptr
|
||||
? m_framePlanBuilder->BuildStackPlans(
|
||||
requests,
|
||||
GetPipelineAsset())
|
||||
: std::vector<CameraStackFramePlan>();
|
||||
}
|
||||
|
||||
bool RenderPipelineHost::Render(const CameraFramePlan& plan) {
|
||||
return m_cameraRenderer.Render(plan);
|
||||
std::vector<CameraFramePlan> plans = {};
|
||||
plans.push_back(plan);
|
||||
return Render(
|
||||
CameraStackFramePlan::FromCameraPlans(
|
||||
plan.request.cameraStackId,
|
||||
std::move(plans)));
|
||||
}
|
||||
|
||||
bool RenderPipelineHost::Render(const std::vector<CameraFramePlan>& plans) {
|
||||
@@ -63,8 +97,20 @@ bool RenderPipelineHost::Render(const std::vector<CameraFramePlan>& plans) {
|
||||
sortedPlans.end(),
|
||||
CompareCameraFramePlans);
|
||||
|
||||
return Render(
|
||||
m_framePlanBuilder != nullptr
|
||||
? m_framePlanBuilder->BuildStackPlansFromFramePlans(
|
||||
std::move(sortedPlans))
|
||||
: std::vector<CameraStackFramePlan>());
|
||||
}
|
||||
|
||||
bool RenderPipelineHost::Render(const CameraStackFramePlan& stackPlan) {
|
||||
if (!stackPlan.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rendered = false;
|
||||
for (const CameraFramePlan& plan : sortedPlans) {
|
||||
for (const CameraFramePlan& plan : stackPlan.cameraPlans) {
|
||||
if (!m_cameraRenderer.Render(plan)) {
|
||||
return false;
|
||||
}
|
||||
@@ -72,6 +118,44 @@ bool RenderPipelineHost::Render(const std::vector<CameraFramePlan>& plans) {
|
||||
rendered = true;
|
||||
}
|
||||
|
||||
if (rendered) {
|
||||
if (RenderPipeline* const pipeline = GetPipeline();
|
||||
pipeline != nullptr) {
|
||||
pipeline->FinishCameraStackRendering(stackPlan);
|
||||
}
|
||||
}
|
||||
|
||||
return rendered;
|
||||
}
|
||||
|
||||
bool RenderPipelineHost::Render(const std::vector<CameraStackFramePlan>& stackPlans) {
|
||||
if (stackPlans.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const CameraStackFramePlan& stackPlan : stackPlans) {
|
||||
if (!stackPlan.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CameraStackFramePlan> sortedStackPlans =
|
||||
stackPlans;
|
||||
std::stable_sort(
|
||||
sortedStackPlans.begin(),
|
||||
sortedStackPlans.end(),
|
||||
CompareCameraStackFramePlans);
|
||||
|
||||
bool rendered = false;
|
||||
for (const CameraStackFramePlan& stackPlan :
|
||||
sortedStackPlans) {
|
||||
if (!Render(stackPlan)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rendered = true;
|
||||
}
|
||||
|
||||
return rendered;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,21 @@ std::vector<CameraFramePlan> SceneRenderer::BuildFramePlans(
|
||||
return m_pipelineHost.BuildFramePlans(requests);
|
||||
}
|
||||
|
||||
std::vector<CameraStackFramePlan> SceneRenderer::BuildStackFramePlans(
|
||||
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,
|
||||
m_pipelineHost.GetPipelineAsset());
|
||||
return m_pipelineHost.BuildStackFramePlans(requests);
|
||||
}
|
||||
|
||||
bool SceneRenderer::Render(const CameraFramePlan& plan) {
|
||||
return m_pipelineHost.Render(plan);
|
||||
}
|
||||
@@ -42,12 +57,20 @@ bool SceneRenderer::Render(const std::vector<CameraFramePlan>& plans) {
|
||||
return m_pipelineHost.Render(plans);
|
||||
}
|
||||
|
||||
bool SceneRenderer::Render(const CameraStackFramePlan& stackPlan) {
|
||||
return m_pipelineHost.Render(stackPlan);
|
||||
}
|
||||
|
||||
bool SceneRenderer::Render(const std::vector<CameraStackFramePlan>& stackPlans) {
|
||||
return m_pipelineHost.Render(stackPlans);
|
||||
}
|
||||
|
||||
bool SceneRenderer::Render(
|
||||
const Components::Scene& scene,
|
||||
Components::CameraComponent* overrideCamera,
|
||||
const RenderContext& context,
|
||||
const RenderSurface& surface) {
|
||||
return Render(BuildFramePlans(scene, overrideCamera, context, surface));
|
||||
return Render(BuildStackFramePlans(scene, overrideCamera, context, surface));
|
||||
}
|
||||
|
||||
} // namespace Rendering
|
||||
|
||||
@@ -193,6 +193,13 @@ bool ScriptableRenderPipelineHost::Render(
|
||||
m_pipelineBackend->Render(context, surface, sceneData);
|
||||
}
|
||||
|
||||
void ScriptableRenderPipelineHost::FinishCameraStackRendering(
|
||||
const CameraStackFramePlan& stackPlan) {
|
||||
if (m_stageRecorder != nullptr) {
|
||||
m_stageRecorder->FinishCameraStackRendering(stackPlan);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptableRenderPipelineHost::ConfigureRenderSceneData(
|
||||
const CameraFramePlan& plan,
|
||||
RenderSceneData& sceneData) const {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "Rendering/RenderPipelineAsset.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
@@ -13,6 +15,19 @@ std::vector<CameraFramePlan> CameraFramePlanBuilder::BuildPlans(
|
||||
return plans;
|
||||
}
|
||||
|
||||
std::vector<CameraStackFramePlan> CameraFramePlanBuilder::BuildStackPlans(
|
||||
const std::vector<CameraRenderRequest>& requests,
|
||||
const RenderPipelineAsset* pipelineAsset) {
|
||||
std::vector<CameraFramePlan> plans = CreatePlansFromRequests(requests);
|
||||
ConfigurePlans(plans, pipelineAsset);
|
||||
return CreateStackPlansFromPlans(std::move(plans));
|
||||
}
|
||||
|
||||
std::vector<CameraStackFramePlan> CameraFramePlanBuilder::BuildStackPlansFromFramePlans(
|
||||
std::vector<CameraFramePlan> plans) const {
|
||||
return CreateStackPlansFromPlans(std::move(plans));
|
||||
}
|
||||
|
||||
std::vector<CameraFramePlan> CameraFramePlanBuilder::CreatePlansFromRequests(
|
||||
const std::vector<CameraRenderRequest>& requests) const {
|
||||
std::vector<CameraFramePlan> plans = {};
|
||||
@@ -24,6 +39,43 @@ std::vector<CameraFramePlan> CameraFramePlanBuilder::CreatePlansFromRequests(
|
||||
return plans;
|
||||
}
|
||||
|
||||
std::vector<CameraStackFramePlan> CameraFramePlanBuilder::CreateStackPlansFromPlans(
|
||||
std::vector<CameraFramePlan> plans) const {
|
||||
std::vector<CameraStackFramePlan> stackPlans = {};
|
||||
if (plans.empty()) {
|
||||
return stackPlans;
|
||||
}
|
||||
|
||||
stackPlans.reserve(plans.size());
|
||||
|
||||
for (CameraFramePlan& plan : plans) {
|
||||
const uint64_t cameraStackId = plan.request.cameraStackId;
|
||||
if (cameraStackId != 0u) {
|
||||
bool appendedToExistingStack = false;
|
||||
for (CameraStackFramePlan& stackPlan : stackPlans) {
|
||||
if (stackPlan.cameraStackId == cameraStackId) {
|
||||
stackPlan.cameraPlans.push_back(std::move(plan));
|
||||
appendedToExistingStack = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (appendedToExistingStack) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CameraFramePlan> stackCameraPlans = {};
|
||||
stackCameraPlans.push_back(std::move(plan));
|
||||
stackPlans.push_back(
|
||||
CameraStackFramePlan::FromCameraPlans(
|
||||
cameraStackId,
|
||||
std::move(stackCameraPlans)));
|
||||
}
|
||||
|
||||
return stackPlans;
|
||||
}
|
||||
|
||||
void CameraFramePlanBuilder::ConfigurePlans(
|
||||
std::vector<CameraFramePlan>& plans,
|
||||
const RenderPipelineAsset* pipelineAsset) const {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/Execution/CameraFramePlan.h>
|
||||
#include <XCEngine/Rendering/Execution/CameraStackFramePlan.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -19,10 +20,17 @@ public:
|
||||
std::vector<CameraFramePlan> BuildPlans(
|
||||
const std::vector<CameraRenderRequest>& requests,
|
||||
const RenderPipelineAsset* pipelineAsset);
|
||||
std::vector<CameraStackFramePlan> BuildStackPlans(
|
||||
const std::vector<CameraRenderRequest>& requests,
|
||||
const RenderPipelineAsset* pipelineAsset);
|
||||
std::vector<CameraStackFramePlan> BuildStackPlansFromFramePlans(
|
||||
std::vector<CameraFramePlan> plans) const;
|
||||
|
||||
private:
|
||||
std::vector<CameraFramePlan> CreatePlansFromRequests(
|
||||
const std::vector<CameraRenderRequest>& requests) const;
|
||||
std::vector<CameraStackFramePlan> CreateStackPlansFromPlans(
|
||||
std::vector<CameraFramePlan> plans) const;
|
||||
void ConfigurePlans(
|
||||
std::vector<CameraFramePlan>& plans,
|
||||
const RenderPipelineAsset* pipelineAsset) const;
|
||||
|
||||
@@ -56,6 +56,8 @@ std::vector<CameraRenderRequest> SceneRenderRequestPlanner::BuildRequests(
|
||||
CollectCameras(scene, overrideCamera);
|
||||
|
||||
size_t renderedBaseCameraCount = 0;
|
||||
uint64_t nextCameraStackId = 1u;
|
||||
uint64_t activeCameraStackId = 0u;
|
||||
for (Components::CameraComponent* camera : cameras) {
|
||||
CameraRenderRequest request;
|
||||
if (!SceneRenderRequestUtils::BuildCameraRenderRequest(
|
||||
@@ -69,6 +71,12 @@ std::vector<CameraRenderRequest> SceneRenderRequestPlanner::BuildRequests(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (camera->GetStackType() == Components::CameraStackType::Base ||
|
||||
activeCameraStackId == 0u) {
|
||||
activeCameraStackId = nextCameraStackId++;
|
||||
}
|
||||
request.cameraStackId = activeCameraStackId;
|
||||
|
||||
if (pipelineAsset != nullptr) {
|
||||
pipelineAsset->ConfigureCameraRenderRequest(
|
||||
request,
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "Physics/PhysicsWorld.h"
|
||||
#include "Rendering/Caches/DirectionalShadowSurfaceCache.h"
|
||||
#include "Rendering/Execution/CameraFramePlan.h"
|
||||
#include "Rendering/Execution/CameraStackFramePlan.h"
|
||||
#include "Rendering/Execution/DirectionalShadowExecutionState.h"
|
||||
#include "Rendering/Execution/CameraFrameRenderGraphFrameData.h"
|
||||
#include "Rendering/Execution/Internal/CameraFrameGraph/SurfaceUtils.h"
|
||||
@@ -2134,6 +2135,7 @@ public:
|
||||
m_supportsStageContextualMethod = nullptr;
|
||||
m_supportsStageMethod = nullptr;
|
||||
m_recordStageMethod = nullptr;
|
||||
m_finishCameraStackRenderingMethod = nullptr;
|
||||
m_resolvedPipelineHandle = 0;
|
||||
m_boundSceneDrawBackend = nullptr;
|
||||
}
|
||||
@@ -2322,6 +2324,35 @@ public:
|
||||
return flushed;
|
||||
}
|
||||
|
||||
void FinishCameraStackRendering(
|
||||
const Rendering::CameraStackFramePlan& stackPlan) override {
|
||||
if (!IsRuntimeAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MonoObject* const pipelineObject = GetManagedPipelineObject();
|
||||
MonoMethod* const method =
|
||||
ResolveFinishCameraStackRenderingMethod(pipelineObject);
|
||||
if (pipelineObject == nullptr ||
|
||||
method == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const Rendering::CameraFramePlan& plan :
|
||||
stackPlan.cameraPlans) {
|
||||
int32_t rendererIndex = plan.request.rendererIndex;
|
||||
uint64_t framePlanId = plan.framePlanId;
|
||||
void* args[2] = {
|
||||
&rendererIndex,
|
||||
&framePlanId };
|
||||
m_runtime->InvokeManagedMethod(
|
||||
pipelineObject,
|
||||
method,
|
||||
args,
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct ManagedRenderFuncExecutionState {
|
||||
std::shared_ptr<const MonoManagedRenderPipelineAssetRuntime>
|
||||
@@ -2415,6 +2446,7 @@ private:
|
||||
m_supportsStageContextualMethod = nullptr;
|
||||
m_supportsStageMethod = nullptr;
|
||||
m_recordStageMethod = nullptr;
|
||||
m_finishCameraStackRenderingMethod = nullptr;
|
||||
m_resolvedPipelineHandle = pipelineHandle;
|
||||
}
|
||||
|
||||
@@ -2471,6 +2503,19 @@ private:
|
||||
return m_recordStageMethod;
|
||||
}
|
||||
|
||||
MonoMethod* ResolveFinishCameraStackRenderingMethod(
|
||||
MonoObject* pipelineObject) const {
|
||||
if (m_finishCameraStackRenderingMethod == nullptr) {
|
||||
m_finishCameraStackRenderingMethod =
|
||||
m_runtime->ResolveManagedMethod(
|
||||
pipelineObject,
|
||||
"FinishCameraStackRenderingInstance",
|
||||
2);
|
||||
}
|
||||
|
||||
return m_finishCameraStackRenderingMethod;
|
||||
}
|
||||
|
||||
bool FlushManagedRasterPasses(
|
||||
const Rendering::RenderPipelineStageRenderGraphContext& context,
|
||||
ManagedScriptableRenderContextState& managedContextState) {
|
||||
@@ -2653,6 +2698,7 @@ private:
|
||||
mutable MonoMethod* m_supportsStageContextualMethod = nullptr;
|
||||
mutable MonoMethod* m_supportsStageMethod = nullptr;
|
||||
mutable MonoMethod* m_recordStageMethod = nullptr;
|
||||
mutable MonoMethod* m_finishCameraStackRenderingMethod = nullptr;
|
||||
mutable uint32_t m_resolvedPipelineHandle = 0;
|
||||
std::vector<std::unique_ptr<Rendering::RenderPass>> m_fullscreenPassPool = {};
|
||||
Rendering::SceneDrawBackend* m_boundSceneDrawBackend = nullptr;
|
||||
|
||||
@@ -46,6 +46,12 @@ namespace XCEngine.Rendering.Universal
|
||||
private readonly Dictionary<ulong, RendererFramePlan>
|
||||
m_framePlans =
|
||||
new Dictionary<ulong, RendererFramePlan>();
|
||||
private readonly List<ScriptableRenderPass>
|
||||
m_pendingCameraStackFinishPasses =
|
||||
new List<ScriptableRenderPass>();
|
||||
private readonly List<ulong>
|
||||
m_pendingCameraStackFinishFramePlans =
|
||||
new List<ulong>();
|
||||
private readonly CommandBuffer m_finishCameraStackCommandBuffer =
|
||||
new CommandBuffer("ScriptableRenderer.FinishCameraStack");
|
||||
private bool m_disposed;
|
||||
@@ -76,6 +82,8 @@ namespace XCEngine.Rendering.Universal
|
||||
m_activePassQueue.Clear();
|
||||
m_rendererBlocks.Clear();
|
||||
m_framePlans.Clear();
|
||||
m_pendingCameraStackFinishPasses.Clear();
|
||||
m_pendingCameraStackFinishFramePlans.Clear();
|
||||
m_disposed = true;
|
||||
}
|
||||
|
||||
@@ -185,6 +193,12 @@ namespace XCEngine.Rendering.Universal
|
||||
return RecordRenderer(context);
|
||||
}
|
||||
|
||||
internal void FinishCameraStackRenderingInstance(
|
||||
RendererRecordingContext context)
|
||||
{
|
||||
FinishCameraStackRendering(context);
|
||||
}
|
||||
|
||||
protected virtual bool SupportsRendererRecording(
|
||||
RendererRecordingContext context)
|
||||
{
|
||||
@@ -267,7 +281,10 @@ namespace XCEngine.Rendering.Universal
|
||||
}
|
||||
if (recorded)
|
||||
{
|
||||
FinishCameraStackRendering();
|
||||
QueueCameraStackFinish(
|
||||
m_activePassQueue,
|
||||
0UL);
|
||||
FinishCameraStackRendering(null);
|
||||
}
|
||||
|
||||
return recorded;
|
||||
@@ -814,7 +831,7 @@ namespace XCEngine.Rendering.Universal
|
||||
}
|
||||
if (recorded)
|
||||
{
|
||||
FinishCameraStackRendering();
|
||||
QueueCameraStackFinish(framePlan);
|
||||
}
|
||||
|
||||
return recorded;
|
||||
@@ -897,12 +914,71 @@ namespace XCEngine.Rendering.Universal
|
||||
m_rendererBlocks.Clear();
|
||||
}
|
||||
|
||||
private void FinishCameraStackRendering()
|
||||
private void QueueCameraStackFinish(
|
||||
RendererFramePlan framePlan)
|
||||
{
|
||||
for (int i = 0; i < m_activePassQueue.Count; ++i)
|
||||
if (framePlan == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QueueCameraStackFinish(
|
||||
framePlan.activePassQueue,
|
||||
framePlan.framePlanId);
|
||||
}
|
||||
|
||||
private void QueueCameraStackFinish(
|
||||
IList<ScriptableRenderPass> renderPasses,
|
||||
ulong framePlanId)
|
||||
{
|
||||
if (renderPasses == null ||
|
||||
renderPasses.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (framePlanId != 0UL &&
|
||||
m_pendingCameraStackFinishFramePlans.Contains(
|
||||
framePlanId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (framePlanId != 0UL)
|
||||
{
|
||||
m_pendingCameraStackFinishFramePlans.Add(
|
||||
framePlanId);
|
||||
}
|
||||
|
||||
for (int i = 0; i < renderPasses.Count; ++i)
|
||||
{
|
||||
ScriptableRenderPass renderPass =
|
||||
m_activePassQueue[i];
|
||||
renderPasses[i];
|
||||
if (renderPass != null)
|
||||
{
|
||||
m_pendingCameraStackFinishPasses.Add(
|
||||
renderPass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FinishCameraStackRendering(
|
||||
RendererRecordingContext context)
|
||||
{
|
||||
if (context != null &&
|
||||
context.framePlanId != 0UL &&
|
||||
!m_pendingCameraStackFinishFramePlans.Contains(
|
||||
context.framePlanId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0;
|
||||
i < m_pendingCameraStackFinishPasses.Count;
|
||||
++i)
|
||||
{
|
||||
ScriptableRenderPass renderPass =
|
||||
m_pendingCameraStackFinishPasses[i];
|
||||
if (renderPass == null)
|
||||
{
|
||||
continue;
|
||||
@@ -911,6 +987,9 @@ namespace XCEngine.Rendering.Universal
|
||||
renderPass.OnFinishCameraStackRendering(
|
||||
m_finishCameraStackCommandBuffer);
|
||||
}
|
||||
|
||||
m_pendingCameraStackFinishPasses.Clear();
|
||||
m_pendingCameraStackFinishFramePlans.Clear();
|
||||
}
|
||||
|
||||
protected virtual void ReleaseRuntimeResources()
|
||||
|
||||
@@ -63,6 +63,24 @@ namespace XCEngine.Rendering.Universal
|
||||
renderer.RecordRendererInstance(recordingContext);
|
||||
}
|
||||
|
||||
protected override void FinishCameraStackRendering(
|
||||
int rendererIndex,
|
||||
ulong framePlanId)
|
||||
{
|
||||
ScriptableRenderer renderer =
|
||||
ResolveRenderer(rendererIndex);
|
||||
if (renderer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
renderer.FinishCameraStackRenderingInstance(
|
||||
new RendererRecordingContext(
|
||||
CameraFrameStage.MainScene,
|
||||
rendererIndex,
|
||||
framePlanId));
|
||||
}
|
||||
|
||||
private ScriptableRenderer ResolveRenderer(
|
||||
int rendererIndex)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,15 @@ namespace XCEngine.Rendering
|
||||
m_disposed = true;
|
||||
}
|
||||
|
||||
internal void FinishCameraStackRenderingInstance(
|
||||
int rendererIndex,
|
||||
ulong framePlanId)
|
||||
{
|
||||
FinishCameraStackRendering(
|
||||
rendererIndex,
|
||||
framePlanId);
|
||||
}
|
||||
|
||||
protected virtual bool SupportsStageRenderGraph(
|
||||
CameraFrameStage stage)
|
||||
{
|
||||
@@ -50,6 +59,12 @@ namespace XCEngine.Rendering
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void FinishCameraStackRendering(
|
||||
int rendererIndex,
|
||||
ulong framePlanId)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void Dispose(
|
||||
bool disposing)
|
||||
{
|
||||
|
||||
@@ -71,6 +71,7 @@ struct MockPipelineState {
|
||||
int recordPostProcessCalls = 0;
|
||||
int recordFinalOutputCalls = 0;
|
||||
int executeRecordedMainSceneCalls = 0;
|
||||
int finishCameraStackRenderingCalls = 0;
|
||||
bool renderResult = true;
|
||||
bool supportsMainSceneRenderGraph = false;
|
||||
bool supportsPostProcessRenderGraph = false;
|
||||
@@ -112,6 +113,7 @@ struct MockPipelineState {
|
||||
std::vector<CameraComponent*> renderedCameras;
|
||||
std::vector<RenderClearFlags> renderedClearFlags;
|
||||
std::vector<XCEngine::Math::Color> renderedClearColors;
|
||||
std::vector<size_t> finishedCameraStackSizes;
|
||||
std::vector<std::string> eventLog;
|
||||
};
|
||||
|
||||
@@ -764,6 +766,13 @@ public:
|
||||
return m_state->renderResult;
|
||||
}
|
||||
|
||||
void FinishCameraStackRendering(
|
||||
const CameraStackFramePlan& stackPlan) override {
|
||||
++m_state->finishCameraStackRenderingCalls;
|
||||
m_state->finishedCameraStackSizes.push_back(
|
||||
stackPlan.cameraPlans.size());
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<MockPipelineState> m_state;
|
||||
};
|
||||
@@ -3420,15 +3429,65 @@ TEST(SceneRenderer_Test, RendersBaseCamerasBeforeOverlayCamerasAndResolvesAutoCl
|
||||
ASSERT_EQ(plans.size(), 3u);
|
||||
EXPECT_EQ(plans[0].request.camera, earlyBaseCamera);
|
||||
EXPECT_EQ(plans[0].request.cameraStackOrder, 0u);
|
||||
EXPECT_NE(plans[0].request.cameraStackId, 0u);
|
||||
EXPECT_EQ(plans[0].request.clearFlags, RenderClearFlags::All);
|
||||
EXPECT_EQ(plans[1].request.camera, lateBaseCamera);
|
||||
EXPECT_EQ(plans[1].request.cameraStackOrder, 0u);
|
||||
EXPECT_NE(plans[1].request.cameraStackId, 0u);
|
||||
EXPECT_NE(
|
||||
plans[0].request.cameraStackId,
|
||||
plans[1].request.cameraStackId);
|
||||
EXPECT_EQ(plans[1].request.clearFlags, RenderClearFlags::Depth);
|
||||
EXPECT_EQ(plans[2].request.camera, overlayCamera);
|
||||
EXPECT_EQ(plans[2].request.cameraStackOrder, 1u);
|
||||
EXPECT_EQ(
|
||||
plans[2].request.cameraStackId,
|
||||
plans[1].request.cameraStackId);
|
||||
EXPECT_EQ(plans[2].request.clearFlags, RenderClearFlags::Depth);
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, BuildsRootCameraStackFramePlansFromSceneRequests) {
|
||||
Scene scene("SceneRendererCameraStackFramePlanScene");
|
||||
|
||||
GameObject* firstBaseCameraObject = scene.CreateGameObject("FirstBaseCamera");
|
||||
auto* firstBaseCamera = firstBaseCameraObject->AddComponent<CameraComponent>();
|
||||
firstBaseCamera->SetDepth(1.0f);
|
||||
firstBaseCamera->SetStackType(CameraStackType::Base);
|
||||
|
||||
GameObject* secondBaseCameraObject = scene.CreateGameObject("SecondBaseCamera");
|
||||
auto* secondBaseCamera = secondBaseCameraObject->AddComponent<CameraComponent>();
|
||||
secondBaseCamera->SetDepth(2.0f);
|
||||
secondBaseCamera->SetStackType(CameraStackType::Base);
|
||||
|
||||
GameObject* overlayCameraObject = scene.CreateGameObject("OverlayCamera");
|
||||
auto* overlayCamera = overlayCameraObject->AddComponent<CameraComponent>();
|
||||
overlayCamera->SetDepth(3.0f);
|
||||
overlayCamera->SetStackType(CameraStackType::Overlay);
|
||||
|
||||
SceneRenderer renderer;
|
||||
const std::vector<CameraStackFramePlan> stackPlans =
|
||||
renderer.BuildStackFramePlans(
|
||||
scene,
|
||||
nullptr,
|
||||
CreateValidContext(),
|
||||
RenderSurface(640, 360));
|
||||
|
||||
ASSERT_EQ(stackPlans.size(), 2u);
|
||||
ASSERT_EQ(stackPlans[0].cameraPlans.size(), 1u);
|
||||
EXPECT_EQ(stackPlans[0].cameraPlans[0].request.camera, firstBaseCamera);
|
||||
EXPECT_TRUE(stackPlans[0].HasBaseCamera());
|
||||
EXPECT_FALSE(stackPlans[0].IsOverlayOnly());
|
||||
|
||||
ASSERT_EQ(stackPlans[1].cameraPlans.size(), 2u);
|
||||
EXPECT_EQ(stackPlans[1].cameraPlans[0].request.camera, secondBaseCamera);
|
||||
EXPECT_EQ(stackPlans[1].cameraPlans[1].request.camera, overlayCamera);
|
||||
EXPECT_TRUE(stackPlans[1].HasBaseCamera());
|
||||
EXPECT_FALSE(stackPlans[1].IsOverlayOnly());
|
||||
EXPECT_EQ(
|
||||
stackPlans[1].cameraPlans[1].request.cameraStackId,
|
||||
stackPlans[1].cameraPlans[0].request.cameraStackId);
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, PreservesSceneTraversalOrderForEqualPriorityCameras) {
|
||||
Scene scene("SceneRendererStableSceneOrder");
|
||||
|
||||
@@ -4531,6 +4590,55 @@ TEST(RenderPipelineHost_Test, ForwardsPipelineAssetLifetimeAndRenderCallsToCamer
|
||||
EXPECT_EQ(replacementState->shutdownCalls, 1);
|
||||
}
|
||||
|
||||
TEST(RenderPipelineHost_Test, RendersCameraStackFramePlansAsSingleStackLifetime) {
|
||||
Scene scene("RenderPipelineHostStackPlans");
|
||||
|
||||
GameObject* baseCameraObject = scene.CreateGameObject("BaseCamera");
|
||||
auto* baseCamera = baseCameraObject->AddComponent<CameraComponent>();
|
||||
baseCamera->SetDepth(1.0f);
|
||||
baseCamera->SetStackType(CameraStackType::Base);
|
||||
|
||||
GameObject* overlayCameraObject = scene.CreateGameObject("OverlayCamera");
|
||||
auto* overlayCamera = overlayCameraObject->AddComponent<CameraComponent>();
|
||||
overlayCamera->SetDepth(2.0f);
|
||||
overlayCamera->SetStackType(CameraStackType::Overlay);
|
||||
|
||||
CameraRenderRequest baseRequest = {};
|
||||
baseRequest.scene = &scene;
|
||||
baseRequest.camera = baseCamera;
|
||||
baseRequest.context = CreateValidContext();
|
||||
baseRequest.surface = RenderSurface(800, 600);
|
||||
baseRequest.cameraStackId = 42u;
|
||||
baseRequest.cameraDepth = baseCamera->GetDepth();
|
||||
baseRequest.cameraStackOrder = 0u;
|
||||
baseRequest.clearFlags = RenderClearFlags::All;
|
||||
|
||||
CameraRenderRequest overlayRequest = baseRequest;
|
||||
overlayRequest.camera = overlayCamera;
|
||||
overlayRequest.cameraDepth = overlayCamera->GetDepth();
|
||||
overlayRequest.cameraStackOrder = 1u;
|
||||
overlayRequest.clearFlags = RenderClearFlags::Depth;
|
||||
|
||||
auto state = std::make_shared<MockPipelineState>();
|
||||
RenderPipelineHost host(MakeMockPipelineAsset(state));
|
||||
|
||||
const std::vector<CameraStackFramePlan> stackPlans =
|
||||
host.BuildStackFramePlans({ baseRequest, overlayRequest });
|
||||
ASSERT_EQ(stackPlans.size(), 1u);
|
||||
ASSERT_EQ(stackPlans[0].cameraPlans.size(), 2u);
|
||||
EXPECT_EQ(stackPlans[0].cameraStackId, 42u);
|
||||
EXPECT_EQ(stackPlans[0].cameraPlans[0].request.camera, baseCamera);
|
||||
EXPECT_EQ(stackPlans[0].cameraPlans[1].request.camera, overlayCamera);
|
||||
|
||||
ASSERT_TRUE(host.Render(stackPlans));
|
||||
ASSERT_EQ(state->renderedCameras.size(), 2u);
|
||||
EXPECT_EQ(state->renderedCameras[0], baseCamera);
|
||||
EXPECT_EQ(state->renderedCameras[1], overlayCamera);
|
||||
EXPECT_EQ(state->finishCameraStackRenderingCalls, 1);
|
||||
ASSERT_EQ(state->finishedCameraStackSizes.size(), 1u);
|
||||
EXPECT_EQ(state->finishedCameraStackSizes[0], 2u);
|
||||
}
|
||||
|
||||
TEST(RenderPipelineHost_Test, SortsManualFramePlansByDepthBeforeRendering) {
|
||||
Scene scene("RenderPipelineHostManualRequests");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user