Compare commits
6 Commits
c3d443eb85
...
e83f911aef
| Author | SHA1 | Date | |
|---|---|---|---|
| e83f911aef | |||
| dd2299c8b0 | |||
| b8d29e39f6 | |||
| 72914b3865 | |||
| e6950fa704 | |||
| 21b0530f7b |
283
docs/plan/NewEditor_方案1无边框宿主采用计划_2026-04-14.md
Normal file
283
docs/plan/NewEditor_方案1无边框宿主采用计划_2026-04-14.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# NewEditor 方案1无边框宿主采用计划
|
||||
|
||||
日期: `2026-04-14`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
把 `new_editor` 从当前的原生 `WS_OVERLAPPEDWINDOW + DWM + HWND swapchain` 宿主模式,重构为:
|
||||
|
||||
- 无边框顶层窗口
|
||||
- 自绘标题栏与边框
|
||||
- 应用自己接管 move / resize / maximize / restore / hit-test
|
||||
- 宿主渲染链与窗口尺寸变化由同一套状态机驱动
|
||||
|
||||
核心目的只有一个:
|
||||
|
||||
尽可能消除当前原生窗口 live resize 期间那种“旧帧被系统先拉伸,再等新帧补上”的视觉变形。
|
||||
|
||||
## 2. 为什么必须走这条路
|
||||
|
||||
当前路径的问题不是 XCUI 布局树本身,而是宿主显示链顺序决定的:
|
||||
|
||||
1. Windows 先改变原生窗口外框尺寸。
|
||||
2. DWM 立即需要一张图去填新窗口区域。
|
||||
3. `new_editor` 再收到 `WM_SIZE`,之后才开始:
|
||||
- `ResizeBuffers`
|
||||
- backbuffer / interop target 处理
|
||||
- UI + viewport 合成
|
||||
- `Present`
|
||||
4. 这中间只要慢一个 compositor tick,就会看到旧尺寸帧被拉伸。
|
||||
|
||||
只要继续使用原生 non-client resize,这个问题就只能减轻,不能彻底归零。
|
||||
|
||||
## 3. 方案1的总思路
|
||||
|
||||
不要再让系统驱动 resize 交互。
|
||||
|
||||
改为:
|
||||
|
||||
1. 窗口本身使用无边框样式。
|
||||
2. 顶部标题栏、拖动区、8 个 resize grip 全部放到 client area。
|
||||
3. 鼠标拖动边界时,不进入系统的 `WM_ENTERSIZEMOVE` 模态循环。
|
||||
4. 应用自己维护一套 `WindowFrameController`:
|
||||
- 记录拖动起点
|
||||
- 计算目标外框矩形
|
||||
- 先驱动宿主渲染链切到目标尺寸
|
||||
- 再提交窗口矩形变化与新帧 present
|
||||
|
||||
这样窗口尺寸变化、swapchain 尺寸变化、UI 布局变化、present 节奏都由应用自己掌控,而不是被 Windows 原生外框拆成两段。
|
||||
|
||||
## 4. 重构边界
|
||||
|
||||
这次重构只动 `new_editor/app/Host` 宿主层,不把业务逻辑塞进去。
|
||||
|
||||
### 4.1 保留不动的层
|
||||
|
||||
- `XCEditor` 基础 UI 组件层
|
||||
- `new_editor/app/Workspace`
|
||||
- `new_editor/app/Panels`
|
||||
- `Viewport` 业务层
|
||||
|
||||
### 4.2 主要改造层
|
||||
|
||||
- [Application.cpp](D:/Xuanchi/Main/XCEngine/new_editor/app/Application.cpp)
|
||||
- [Application.h](D:/Xuanchi/Main/XCEngine/new_editor/app/Application.h)
|
||||
- [WindowMessageDispatcher.cpp](D:/Xuanchi/Main/XCEngine/new_editor/app/Host/WindowMessageDispatcher.cpp)
|
||||
- [WindowMessageDispatcher.h](D:/Xuanchi/Main/XCEngine/new_editor/app/Host/WindowMessageDispatcher.h)
|
||||
- [HostRuntimeState.h](D:/Xuanchi/Main/XCEngine/new_editor/app/Host/HostRuntimeState.h)
|
||||
- [D3D12WindowRenderLoop.cpp](D:/Xuanchi/Main/XCEngine/new_editor/app/Host/D3D12WindowRenderLoop.cpp)
|
||||
- [D3D12WindowRenderer.cpp](D:/Xuanchi/Main/XCEngine/new_editor/app/Host/D3D12WindowRenderer.cpp)
|
||||
- [D3D12WindowSwapChainPresenter.cpp](D:/Xuanchi/Main/XCEngine/new_editor/app/Host/D3D12WindowSwapChainPresenter.cpp)
|
||||
|
||||
### 4.3 新增宿主对象
|
||||
|
||||
建议新增以下文件:
|
||||
|
||||
- `new_editor/app/Host/WindowFrameMetrics.h`
|
||||
- `new_editor/app/Host/WindowFrameController.h`
|
||||
- `new_editor/app/Host/WindowFrameController.cpp`
|
||||
- `new_editor/app/Host/WindowFrameInteractionState.h`
|
||||
- `new_editor/app/Host/BorderlessWindowStyle.h`
|
||||
|
||||
职责划分:
|
||||
|
||||
- `WindowFrameMetrics`
|
||||
- 统一管理标题栏高度、resize 边缘厚度、阴影扩展、caption button 区域
|
||||
- `WindowFrameInteractionState`
|
||||
- 记录当前是否在 move / resize
|
||||
- 记录激活边、起始窗口矩形、起始鼠标屏幕坐标
|
||||
- `WindowFrameController`
|
||||
- 处理 hit-test、开始拖动、更新拖动、结束拖动
|
||||
- 计算目标窗口矩形
|
||||
- 输出“本帧宿主需要切到什么尺寸”
|
||||
- `BorderlessWindowStyle`
|
||||
- 集中处理 `WS_POPUP / WS_THICKFRAME / WS_CAPTION` 等样式组合与 DWM 扩展
|
||||
|
||||
## 5. 分阶段执行
|
||||
|
||||
## 阶段 A:把窗口宿主从原生外框切到无边框
|
||||
|
||||
### 目标
|
||||
|
||||
先完成“视觉上还是一个正常窗口,但 non-client 已经不再由系统绘制”。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 创建窗口时移除 `WS_OVERLAPPEDWINDOW`,改为适合无边框的样式组合。
|
||||
2. 顶部工具栏正式承担标题栏职责。
|
||||
3. 处理:
|
||||
- 最小化
|
||||
- 最大化
|
||||
- 还原
|
||||
- 关闭
|
||||
4. 保留 Windows 阴影、任务栏行为、Alt+Tab 行为。
|
||||
|
||||
### 验收
|
||||
|
||||
1. 窗口外框由 XCUI 自己绘制。
|
||||
2. 顶部区域可拖动移动窗口。
|
||||
3. 基本窗口控制按钮可用。
|
||||
|
||||
## 阶段 B:接管 hit-test 与 8 向 resize 手势
|
||||
|
||||
### 目标
|
||||
|
||||
不再依赖系统 non-client resize。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 在 client area 内定义:
|
||||
- 左 / 右 / 上 / 下
|
||||
- 左上 / 右上 / 左下 / 右下
|
||||
的 resize 热区。
|
||||
2. 鼠标 hover 时稳定切换对应 cursor。
|
||||
3. 鼠标按下后进入 `WindowFrameInteractionState`。
|
||||
4. 鼠标移动时由 `WindowFrameController` 计算目标外框矩形。
|
||||
|
||||
### 验收
|
||||
|
||||
1. 8 个方向 resize 都能工作。
|
||||
2. cursor 不闪烁。
|
||||
3. 不再进入 `WM_ENTERSIZEMOVE / WM_EXITSIZEMOVE` 原生模态链。
|
||||
|
||||
## 阶段 C:把窗口尺寸变化改成“应用驱动”
|
||||
|
||||
### 目标
|
||||
|
||||
让窗口矩形变化和新尺寸帧提交进入同一条应用控制链。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 取消当前“完全依赖 `WM_SIZE` 才处理 resize”的模式。
|
||||
2. 鼠标拖动过程中,先由 `WindowFrameController` 产出目标 client size。
|
||||
3. 宿主渲染层按目标 size:
|
||||
- resize swapchain
|
||||
- rebuild backbuffer view
|
||||
- 重新布局 UI
|
||||
- render + present
|
||||
4. 然后再提交窗口矩形更新。
|
||||
|
||||
这里的关键不是“永远先后顺序绝对固定”,而是宿主要从“被动响应 Windows resize”改成“主动推进目标尺寸帧”。
|
||||
|
||||
### 验收
|
||||
|
||||
1. live resize 期间不再有明显的整窗拉伸。
|
||||
2. 内部 UI 不再跟随旧帧一起被整体放大缩小。
|
||||
3. resize 时不黑屏、不闪退。
|
||||
|
||||
## 阶段 D:把 Scene / Game viewport 也纳入同一节奏
|
||||
|
||||
### 目标
|
||||
|
||||
避免窗口外层尺寸变化和 viewport target 更新脱节。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 宿主 resize 状态机输出统一尺寸变更事件。
|
||||
2. `ProductViewportHostService` 消费同一目标尺寸。
|
||||
3. viewport render target 与 host swapchain 的 resize 节奏统一。
|
||||
|
||||
### 验收
|
||||
|
||||
1. 调外层窗口尺寸时,Scene / Game 内部画布不再滞后。
|
||||
2. 不再出现外层变了、内部蓝底画布慢一拍才追上的情况。
|
||||
|
||||
## 阶段 E:补齐无边框窗口的系统行为
|
||||
|
||||
### 目标
|
||||
|
||||
把“能用”补到“能长期替代正式编辑器宿主”。
|
||||
|
||||
### 任务
|
||||
|
||||
1. 双击标题栏最大化 / 还原。
|
||||
2. 拖到屏幕顶部最大化。
|
||||
3. 屏幕边缘吸附。
|
||||
4. 多显示器 DPI / work area 正确处理。
|
||||
5. 最小窗口尺寸约束。
|
||||
6. 系统菜单与快捷键兼容。
|
||||
|
||||
### 验收
|
||||
|
||||
1. 宿主交互接近常规桌面应用。
|
||||
2. 不因无边框而丢失基本桌面体验。
|
||||
|
||||
## 6. 架构原则
|
||||
|
||||
### 6.1 不允许把窗口交互逻辑混回 `Application`
|
||||
|
||||
`Application` 只能做:
|
||||
|
||||
- 应用生命周期
|
||||
- 编辑器业务装配
|
||||
- 驱动一帧更新与渲染
|
||||
|
||||
窗口移动、边框 hit-test、resize 手势状态机都必须沉淀到 `Host`。
|
||||
|
||||
### 6.2 不允许为了“先跑通”堆事件补丁
|
||||
|
||||
禁止继续沿这些方向堆补丁:
|
||||
|
||||
- `WM_SIZE` 后疯狂补 render
|
||||
- `ValidateRect` / `InvalidateRect` 来回试
|
||||
- `DwmFlush` 之类的消息尾部补丁
|
||||
- 在 resize 过程中乱加全局 GPU idle
|
||||
|
||||
这些都只能缓解,不能把宿主控制权拿回来。
|
||||
|
||||
### 6.3 宿主渲染链必须单向清晰
|
||||
|
||||
目标链路必须收敛成:
|
||||
|
||||
`WindowFrameController -> Host resize state -> swapchain/backbuffer resize -> UI + viewport compose -> present`
|
||||
|
||||
而不是:
|
||||
|
||||
`Windows non-client -> WM_SIZE -> 若干临时补丁 -> 侥幸赶上 compositor`
|
||||
|
||||
## 7. 风险点
|
||||
|
||||
### 7.1 这不是小修
|
||||
|
||||
这是宿主交互模型切换,不是单点 bugfix。
|
||||
|
||||
### 7.2 需要额外补齐桌面窗口语义
|
||||
|
||||
无边框之后,很多系统行为都要自己接:
|
||||
|
||||
- caption drag
|
||||
- 双击最大化
|
||||
- hit-test
|
||||
- snap
|
||||
- monitor work area
|
||||
- DPI 变更
|
||||
|
||||
### 7.3 需要严格做阶段验收
|
||||
|
||||
每一阶段都必须编译并人工验证,不然很容易把宿主架构搞乱。
|
||||
|
||||
## 8. 推荐执行顺序
|
||||
|
||||
1. 阶段 A:无边框窗口切换
|
||||
2. 阶段 B:hit-test / resize 手势
|
||||
3. 阶段 C:应用驱动 resize 主链
|
||||
4. 阶段 D:viewport resize 节奏统一
|
||||
5. 阶段 E:补系统级桌面行为
|
||||
|
||||
## 9. 收口标准
|
||||
|
||||
满足以下条件,才算方案1真正落地:
|
||||
|
||||
1. `new_editor` 已彻底脱离原生 non-client resize。
|
||||
2. 窗口拖边 resize 时,整窗拉伸变形基本消失。
|
||||
3. Scene / Game viewport 尺寸变化与外层窗口同步。
|
||||
4. resize 过程中无黑屏、无崩溃、无 cursor 闪烁。
|
||||
5. 顶部标题栏、最大化、吸附、DPI 行为达到正式编辑器可用水平。
|
||||
|
||||
## 10. 结论
|
||||
|
||||
如果目标是“尽可能彻底消除 resize 变形”,就不该继续在当前原生外框链路上修修补补。
|
||||
|
||||
正确主线是:
|
||||
|
||||
把 `new_editor` 宿主改成无边框、自管标题栏、自管 resize、自管 present 节奏的 editor shell。
|
||||
@@ -101,18 +101,18 @@ inline SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan(
|
||||
inline void ApplySceneViewportRenderPlan(
|
||||
const ViewportRenderTargets& targets,
|
||||
SceneViewportRenderPlan& plan,
|
||||
Rendering::CameraRenderRequest& request) {
|
||||
Rendering::CameraFramePlan& framePlan) {
|
||||
ApplySceneViewportRenderRequestSetup(
|
||||
targets,
|
||||
&plan.postScenePasses,
|
||||
request);
|
||||
framePlan);
|
||||
|
||||
if (plan.HasOverlayPasses()) {
|
||||
request.overlayPasses = &plan.overlayPasses;
|
||||
framePlan.overlayPasses = &plan.overlayPasses;
|
||||
}
|
||||
|
||||
request.hasClearColorOverride = plan.hasClearColorOverride;
|
||||
request.clearColorOverride = plan.clearColorOverride;
|
||||
framePlan.request.hasClearColorOverride = plan.hasClearColorOverride;
|
||||
framePlan.request.clearColorOverride = plan.clearColorOverride;
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "ViewportHostRenderTargets.h"
|
||||
|
||||
#include <XCEngine/Core/Math/Color.h>
|
||||
#include <XCEngine/Rendering/Planning/CameraRenderRequest.h>
|
||||
#include <XCEngine/Rendering/Execution/CameraFramePlan.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -142,31 +142,31 @@ inline SceneViewportSelectionOutlineStyle BuildSceneViewportSelectionOutlineStyl
|
||||
inline void ApplySceneViewportRenderRequestSetup(
|
||||
const ViewportRenderTargets& targets,
|
||||
Rendering::RenderPassSequence* postPasses,
|
||||
Rendering::CameraRenderRequest& request) {
|
||||
request.preScenePasses = nullptr;
|
||||
request.postScenePasses = nullptr;
|
||||
request.overlayPasses = nullptr;
|
||||
request.objectId = {};
|
||||
Rendering::CameraFramePlan& plan) {
|
||||
plan.preScenePasses = nullptr;
|
||||
plan.postScenePasses = nullptr;
|
||||
plan.overlayPasses = nullptr;
|
||||
plan.request.objectId = {};
|
||||
|
||||
if (postPasses != nullptr && postPasses->GetPassCount() > 0) {
|
||||
request.postScenePasses = postPasses;
|
||||
plan.postScenePasses = postPasses;
|
||||
}
|
||||
|
||||
if (targets.objectIdView == nullptr || targets.objectIdDepthView == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
request.objectId.surface = BuildViewportObjectIdSurface(targets);
|
||||
request.objectId.surface.SetRenderArea(request.surface.GetRenderArea());
|
||||
plan.request.objectId.surface = BuildViewportObjectIdSurface(targets);
|
||||
plan.request.objectId.surface.SetRenderArea(plan.request.surface.GetRenderArea());
|
||||
}
|
||||
|
||||
inline void MarkSceneViewportRenderSuccess(
|
||||
ViewportRenderTargets& targets,
|
||||
const Rendering::CameraRenderRequest& request) {
|
||||
const Rendering::CameraFramePlan& plan) {
|
||||
targets.colorState = RHI::ResourceStates::PixelShaderResource;
|
||||
targets.objectIdState = RHI::ResourceStates::PixelShaderResource;
|
||||
targets.selectionMaskState = RHI::ResourceStates::PixelShaderResource;
|
||||
targets.hasValidObjectIdFrame = request.objectId.IsRequested();
|
||||
targets.hasValidObjectIdFrame = plan.request.objectId.IsRequested();
|
||||
}
|
||||
|
||||
inline void MarkGameViewportRenderSuccess(ViewportRenderTargets& targets) {
|
||||
|
||||
@@ -591,9 +591,9 @@ private:
|
||||
SceneViewportRenderState sceneState = {};
|
||||
BuildSceneViewportRenderState(entry, context, sceneState);
|
||||
|
||||
std::vector<Rendering::CameraRenderRequest> requests =
|
||||
m_sceneRenderer->BuildRenderRequests(*scene, m_sceneViewCamera.camera, renderContext, surface);
|
||||
if (requests.empty()) {
|
||||
std::vector<Rendering::CameraFramePlan> plans =
|
||||
m_sceneRenderer->BuildFramePlans(*scene, m_sceneViewCamera.camera, renderContext, surface);
|
||||
if (plans.empty()) {
|
||||
ApplyViewportRenderFailure(
|
||||
entry,
|
||||
renderContext,
|
||||
@@ -602,9 +602,9 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
ApplySceneViewportRenderPlan(entry.renderTargets, sceneState.renderPlan, requests[0]);
|
||||
ApplySceneViewportRenderPlan(entry.renderTargets, sceneState.renderPlan, plans[0]);
|
||||
|
||||
if (!m_sceneRenderer->Render(requests)) {
|
||||
if (!m_sceneRenderer->Render(plans)) {
|
||||
ApplyViewportRenderFailure(
|
||||
entry,
|
||||
renderContext,
|
||||
@@ -613,7 +613,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
MarkSceneViewportRenderSuccess(entry.renderTargets, requests[0]);
|
||||
MarkSceneViewportRenderSuccess(entry.renderTargets, plans[0]);
|
||||
const Core::uint32 pendingAsyncLoads = Resources::ResourceManager::Get().GetAsyncPendingCount();
|
||||
context.GetSceneManager().NotifySceneViewportFramePresented(pendingAsyncLoads);
|
||||
if (entry.statusText.empty()) {
|
||||
|
||||
@@ -479,10 +479,12 @@ 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/CameraFrameStage.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/CameraRenderer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/DirectionalShadowExecutionState.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/DrawSettings.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/FrameExecutionContext.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/FrameStageRenderRequest.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/ScenePhase.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/SceneRenderer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/CullingResults.h
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/Execution/CameraFrameStage.h>
|
||||
#include <XCEngine/Rendering/Planning/CameraRenderRequest.h>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -76,6 +77,21 @@ struct CameraFramePlan {
|
||||
}
|
||||
}
|
||||
|
||||
const ScenePassRenderRequest* GetScenePassRequest(CameraFrameStage stage) const {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::ShadowCaster:
|
||||
return &shadowCaster;
|
||||
case CameraFrameStage::DepthOnly:
|
||||
return &request.depthOnly;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const ObjectIdRenderRequest* GetObjectIdRequest(CameraFrameStage stage) const {
|
||||
return stage == CameraFrameStage::ObjectId ? &request.objectId : nullptr;
|
||||
}
|
||||
|
||||
const RenderSurface& GetMainSceneSurface() const {
|
||||
if (postProcess.IsRequested()) {
|
||||
return postProcess.sourceSurface;
|
||||
@@ -161,7 +177,8 @@ struct CameraFramePlan {
|
||||
}
|
||||
};
|
||||
|
||||
inline CameraRenderRequest BuildLegacyCameraRenderRequest(
|
||||
// Compatibility adapter for callers that still require CameraRenderRequest snapshots.
|
||||
inline CameraRenderRequest BuildCompatibilityCameraRenderRequest(
|
||||
const CameraFramePlan& plan) {
|
||||
CameraRenderRequest request = plan.request;
|
||||
request.shadowCaster = plan.shadowCaster;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
enum class CameraFrameStage : uint8_t {
|
||||
PreScenePasses,
|
||||
ShadowCaster,
|
||||
DepthOnly,
|
||||
MainScene,
|
||||
PostProcess,
|
||||
FinalOutput,
|
||||
ObjectId,
|
||||
PostScenePasses,
|
||||
OverlayPasses
|
||||
};
|
||||
|
||||
struct CameraFrameStageInfo {
|
||||
CameraFrameStage stage = CameraFrameStage::MainScene;
|
||||
const char* name = "";
|
||||
bool runtimeStage = true;
|
||||
};
|
||||
|
||||
inline constexpr std::array<CameraFrameStageInfo, 9> kOrderedCameraFrameStages = { {
|
||||
{ CameraFrameStage::PreScenePasses, "PreScenePasses", false },
|
||||
{ CameraFrameStage::ShadowCaster, "ShadowCaster", true },
|
||||
{ CameraFrameStage::DepthOnly, "DepthOnly", true },
|
||||
{ CameraFrameStage::MainScene, "MainScene", true },
|
||||
{ CameraFrameStage::PostProcess, "PostProcess", true },
|
||||
{ CameraFrameStage::FinalOutput, "FinalOutput", true },
|
||||
{ CameraFrameStage::ObjectId, "ObjectId", false },
|
||||
{ CameraFrameStage::PostScenePasses, "PostScenePasses", false },
|
||||
{ CameraFrameStage::OverlayPasses, "OverlayPasses", false }
|
||||
} };
|
||||
|
||||
inline constexpr const char* GetCameraFrameStageName(CameraFrameStage stage) {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PreScenePasses:
|
||||
return "PreScenePasses";
|
||||
case CameraFrameStage::ShadowCaster:
|
||||
return "ShadowCaster";
|
||||
case CameraFrameStage::DepthOnly:
|
||||
return "DepthOnly";
|
||||
case CameraFrameStage::MainScene:
|
||||
return "MainScene";
|
||||
case CameraFrameStage::PostProcess:
|
||||
return "PostProcess";
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return "FinalOutput";
|
||||
case CameraFrameStage::ObjectId:
|
||||
return "ObjectId";
|
||||
case CameraFrameStage::PostScenePasses:
|
||||
return "PostScenePasses";
|
||||
case CameraFrameStage::OverlayPasses:
|
||||
return "OverlayPasses";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/Planning/CameraRenderRequest.h>
|
||||
#include <XCEngine/Rendering/Execution/FrameStageRenderRequest.h>
|
||||
#include <XCEngine/Rendering/Shadow/DirectionalShadowData.h>
|
||||
|
||||
namespace XCEngine {
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/FrameData/RenderCameraData.h>
|
||||
#include <XCEngine/Rendering/RenderPass.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
class RHIResourceView;
|
||||
} // namespace RHI
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
struct ScenePassRenderRequest {
|
||||
RenderSurface surface;
|
||||
RenderClearFlags clearFlags = RenderClearFlags::Depth;
|
||||
bool hasClearColorOverride = false;
|
||||
Math::Color clearColorOverride = Math::Color::Black();
|
||||
bool hasCameraDataOverride = false;
|
||||
RenderCameraData cameraDataOverride = {};
|
||||
|
||||
bool IsRequested() const {
|
||||
return surface.GetDepthAttachment() != nullptr ||
|
||||
!surface.GetColorAttachments().empty();
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
|
||||
return surface.GetDepthAttachment() != nullptr &&
|
||||
(colorAttachments.empty() || colorAttachments[0] != nullptr) &&
|
||||
surface.GetRenderAreaWidth() > 0 &&
|
||||
surface.GetRenderAreaHeight() > 0;
|
||||
}
|
||||
};
|
||||
|
||||
using DepthOnlyRenderRequest = ScenePassRenderRequest;
|
||||
using ShadowCasterRenderRequest = ScenePassRenderRequest;
|
||||
|
||||
inline bool HasValidColorTarget(const RenderSurface& surface) {
|
||||
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
|
||||
return !colorAttachments.empty() &&
|
||||
colorAttachments[0] != nullptr &&
|
||||
surface.GetRenderAreaWidth() > 0 &&
|
||||
surface.GetRenderAreaHeight() > 0;
|
||||
}
|
||||
|
||||
inline bool HasValidSurfaceSampleDescription(const RenderSurface& surface) {
|
||||
const uint32_t sampleCount = surface.GetSampleCount();
|
||||
const uint32_t sampleQuality = surface.GetSampleQuality();
|
||||
return sampleCount > 0u &&
|
||||
(sampleCount > 1u || sampleQuality == 0u);
|
||||
}
|
||||
|
||||
inline bool HasValidSingleSampleColorSource(const RenderSurface& surface) {
|
||||
return HasValidColorTarget(surface) &&
|
||||
HasValidSurfaceSampleDescription(surface) &&
|
||||
surface.GetSampleCount() == 1u &&
|
||||
surface.GetSampleQuality() == 0u;
|
||||
}
|
||||
|
||||
struct ObjectIdRenderRequest {
|
||||
RenderSurface surface;
|
||||
|
||||
bool IsRequested() const {
|
||||
return !surface.GetColorAttachments().empty();
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
|
||||
return !colorAttachments.empty() &&
|
||||
colorAttachments[0] != nullptr &&
|
||||
surface.GetDepthAttachment() != nullptr &&
|
||||
surface.GetRenderAreaWidth() > 0 &&
|
||||
surface.GetRenderAreaHeight() > 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct FullscreenPassRenderRequest {
|
||||
RenderSurface sourceSurface;
|
||||
RHI::RHIResourceView* sourceColorView = nullptr;
|
||||
RHI::ResourceStates sourceColorState = RHI::ResourceStates::Common;
|
||||
RenderSurface destinationSurface;
|
||||
RenderPassSequence* passes = nullptr;
|
||||
|
||||
size_t GetPassCount() const {
|
||||
return passes != nullptr ? passes->GetPassCount() : 0u;
|
||||
}
|
||||
|
||||
bool IsRequested() const {
|
||||
return passes != nullptr;
|
||||
}
|
||||
|
||||
bool RequiresIntermediateSurface() const {
|
||||
return GetPassCount() > 1u;
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
const bool sourceStateIsUsable =
|
||||
sourceSurface.IsAutoTransitionEnabled() ||
|
||||
sourceColorState == RHI::ResourceStates::PixelShaderResource;
|
||||
return passes != nullptr &&
|
||||
HasValidSingleSampleColorSource(sourceSurface) &&
|
||||
sourceColorView != nullptr &&
|
||||
sourceStateIsUsable &&
|
||||
HasValidColorTarget(destinationSurface) &&
|
||||
HasValidSurfaceSampleDescription(destinationSurface);
|
||||
}
|
||||
};
|
||||
|
||||
using PostProcessRenderRequest = FullscreenPassRenderRequest;
|
||||
using FinalOutputRenderRequest = FullscreenPassRenderRequest;
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
RenderPipeline* GetPipeline() const { return m_cameraRenderer.GetPipeline(); }
|
||||
const RenderPipelineAsset* GetPipelineAsset() const { return m_cameraRenderer.GetPipelineAsset(); }
|
||||
|
||||
// Legacy compatibility adapters retained for callers that still consume CameraRenderRequest.
|
||||
std::vector<CameraRenderRequest> BuildRenderRequests(
|
||||
const Components::Scene& scene,
|
||||
Components::CameraComponent* overrideCamera,
|
||||
@@ -42,6 +43,7 @@ public:
|
||||
const RenderContext& context,
|
||||
const RenderSurface& surface);
|
||||
|
||||
// Legacy compatibility adapters retained for callers that still submit CameraRenderRequest.
|
||||
bool Render(const CameraRenderRequest& request);
|
||||
bool Render(const std::vector<CameraRenderRequest>& requests);
|
||||
bool Render(const CameraFramePlan& plan);
|
||||
@@ -53,7 +55,7 @@ public:
|
||||
const RenderSurface& surface);
|
||||
|
||||
private:
|
||||
std::vector<CameraFramePlan> CreateFramePlansFromRequests(
|
||||
std::vector<CameraFramePlan> CreateFramePlansFromLegacyRequests(
|
||||
const std::vector<CameraRenderRequest>& requests) const;
|
||||
void PrepareOwnedFullscreenStageState(size_t requestCount);
|
||||
void ResolveCameraFinalColorPolicies(
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/FrameData/RenderCameraData.h>
|
||||
#include <XCEngine/Rendering/Execution/FrameStageRenderRequest.h>
|
||||
#include <XCEngine/Rendering/Planning/FinalColorSettings.h>
|
||||
#include <XCEngine/Rendering/RenderContext.h>
|
||||
#include <XCEngine/Rendering/RenderPass.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
#include <XCEngine/Rendering/Shadow/DirectionalShadowData.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -16,166 +13,8 @@ class CameraComponent;
|
||||
class Scene;
|
||||
} // namespace Components
|
||||
|
||||
namespace RHI {
|
||||
class RHIResourceView;
|
||||
} // namespace RHI
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
enum class CameraFrameStage : uint8_t {
|
||||
PreScenePasses,
|
||||
ShadowCaster,
|
||||
DepthOnly,
|
||||
MainScene,
|
||||
PostProcess,
|
||||
FinalOutput,
|
||||
ObjectId,
|
||||
PostScenePasses,
|
||||
OverlayPasses
|
||||
};
|
||||
|
||||
struct CameraFrameStageInfo {
|
||||
CameraFrameStage stage = CameraFrameStage::MainScene;
|
||||
const char* name = "";
|
||||
bool runtimeStage = true;
|
||||
};
|
||||
|
||||
inline constexpr std::array<CameraFrameStageInfo, 9> kOrderedCameraFrameStages = {{
|
||||
{ CameraFrameStage::PreScenePasses, "PreScenePasses", false },
|
||||
{ CameraFrameStage::ShadowCaster, "ShadowCaster", true },
|
||||
{ CameraFrameStage::DepthOnly, "DepthOnly", true },
|
||||
{ CameraFrameStage::MainScene, "MainScene", true },
|
||||
{ CameraFrameStage::PostProcess, "PostProcess", true },
|
||||
{ CameraFrameStage::FinalOutput, "FinalOutput", true },
|
||||
{ CameraFrameStage::ObjectId, "ObjectId", false },
|
||||
{ CameraFrameStage::PostScenePasses, "PostScenePasses", false },
|
||||
{ CameraFrameStage::OverlayPasses, "OverlayPasses", false }
|
||||
}};
|
||||
|
||||
inline constexpr const char* GetCameraFrameStageName(CameraFrameStage stage) {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PreScenePasses:
|
||||
return "PreScenePasses";
|
||||
case CameraFrameStage::ShadowCaster:
|
||||
return "ShadowCaster";
|
||||
case CameraFrameStage::DepthOnly:
|
||||
return "DepthOnly";
|
||||
case CameraFrameStage::MainScene:
|
||||
return "MainScene";
|
||||
case CameraFrameStage::PostProcess:
|
||||
return "PostProcess";
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return "FinalOutput";
|
||||
case CameraFrameStage::ObjectId:
|
||||
return "ObjectId";
|
||||
case CameraFrameStage::PostScenePasses:
|
||||
return "PostScenePasses";
|
||||
case CameraFrameStage::OverlayPasses:
|
||||
return "OverlayPasses";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
struct ScenePassRenderRequest {
|
||||
RenderSurface surface;
|
||||
RenderClearFlags clearFlags = RenderClearFlags::Depth;
|
||||
bool hasClearColorOverride = false;
|
||||
Math::Color clearColorOverride = Math::Color::Black();
|
||||
bool hasCameraDataOverride = false;
|
||||
RenderCameraData cameraDataOverride = {};
|
||||
|
||||
bool IsRequested() const {
|
||||
return surface.GetDepthAttachment() != nullptr ||
|
||||
!surface.GetColorAttachments().empty();
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
|
||||
return surface.GetDepthAttachment() != nullptr &&
|
||||
(colorAttachments.empty() || colorAttachments[0] != nullptr) &&
|
||||
surface.GetRenderAreaWidth() > 0 &&
|
||||
surface.GetRenderAreaHeight() > 0;
|
||||
}
|
||||
};
|
||||
|
||||
using DepthOnlyRenderRequest = ScenePassRenderRequest;
|
||||
using ShadowCasterRenderRequest = ScenePassRenderRequest;
|
||||
|
||||
inline bool HasValidColorTarget(const RenderSurface& surface) {
|
||||
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
|
||||
return !colorAttachments.empty() &&
|
||||
colorAttachments[0] != nullptr &&
|
||||
surface.GetRenderAreaWidth() > 0 &&
|
||||
surface.GetRenderAreaHeight() > 0;
|
||||
}
|
||||
|
||||
inline bool HasValidSurfaceSampleDescription(const RenderSurface& surface) {
|
||||
const uint32_t sampleCount = surface.GetSampleCount();
|
||||
const uint32_t sampleQuality = surface.GetSampleQuality();
|
||||
return sampleCount > 0u &&
|
||||
(sampleCount > 1u || sampleQuality == 0u);
|
||||
}
|
||||
|
||||
inline bool HasValidSingleSampleColorSource(const RenderSurface& surface) {
|
||||
return HasValidColorTarget(surface) &&
|
||||
HasValidSurfaceSampleDescription(surface) &&
|
||||
surface.GetSampleCount() == 1u &&
|
||||
surface.GetSampleQuality() == 0u;
|
||||
}
|
||||
|
||||
struct ObjectIdRenderRequest {
|
||||
RenderSurface surface;
|
||||
|
||||
bool IsRequested() const {
|
||||
return !surface.GetColorAttachments().empty();
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
|
||||
return !colorAttachments.empty() &&
|
||||
colorAttachments[0] != nullptr &&
|
||||
surface.GetDepthAttachment() != nullptr &&
|
||||
surface.GetRenderAreaWidth() > 0 &&
|
||||
surface.GetRenderAreaHeight() > 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct FullscreenPassRenderRequest {
|
||||
RenderSurface sourceSurface;
|
||||
RHI::RHIResourceView* sourceColorView = nullptr;
|
||||
RHI::ResourceStates sourceColorState = RHI::ResourceStates::Common;
|
||||
RenderSurface destinationSurface;
|
||||
RenderPassSequence* passes = nullptr;
|
||||
|
||||
size_t GetPassCount() const {
|
||||
return passes != nullptr ? passes->GetPassCount() : 0u;
|
||||
}
|
||||
|
||||
bool IsRequested() const {
|
||||
return passes != nullptr;
|
||||
}
|
||||
|
||||
bool RequiresIntermediateSurface() const {
|
||||
return GetPassCount() > 1u;
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
const bool sourceStateIsUsable =
|
||||
sourceSurface.IsAutoTransitionEnabled() ||
|
||||
sourceColorState == RHI::ResourceStates::PixelShaderResource;
|
||||
return passes != nullptr &&
|
||||
HasValidSingleSampleColorSource(sourceSurface) &&
|
||||
sourceColorView != nullptr &&
|
||||
sourceStateIsUsable &&
|
||||
HasValidColorTarget(destinationSurface) &&
|
||||
HasValidSurfaceSampleDescription(destinationSurface);
|
||||
}
|
||||
};
|
||||
|
||||
using PostProcessRenderRequest = FullscreenPassRenderRequest;
|
||||
using FinalOutputRenderRequest = FullscreenPassRenderRequest;
|
||||
|
||||
struct CameraRenderRequest {
|
||||
const Components::Scene* scene = nullptr;
|
||||
Components::CameraComponent* camera = nullptr;
|
||||
@@ -197,147 +36,6 @@ struct CameraRenderRequest {
|
||||
RenderPassSequence* postScenePasses = nullptr;
|
||||
RenderPassSequence* overlayPasses = nullptr;
|
||||
|
||||
bool HasFrameStage(CameraFrameStage stage) const {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PreScenePasses:
|
||||
return preScenePasses != nullptr;
|
||||
case CameraFrameStage::ShadowCaster:
|
||||
return shadowCaster.IsRequested() || directionalShadow.IsValid();
|
||||
case CameraFrameStage::DepthOnly:
|
||||
return depthOnly.IsRequested();
|
||||
case CameraFrameStage::MainScene:
|
||||
return true;
|
||||
case CameraFrameStage::PostProcess:
|
||||
return postProcess.IsRequested();
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return finalOutput.IsRequested();
|
||||
case CameraFrameStage::ObjectId:
|
||||
return objectId.IsRequested();
|
||||
case CameraFrameStage::PostScenePasses:
|
||||
return postScenePasses != nullptr;
|
||||
case CameraFrameStage::OverlayPasses:
|
||||
return overlayPasses != nullptr;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RenderPassSequence* GetPassSequence(CameraFrameStage stage) const {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PreScenePasses:
|
||||
return preScenePasses;
|
||||
case CameraFrameStage::PostProcess:
|
||||
return postProcess.passes;
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return finalOutput.passes;
|
||||
case CameraFrameStage::PostScenePasses:
|
||||
return postScenePasses;
|
||||
case CameraFrameStage::OverlayPasses:
|
||||
return overlayPasses;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const ScenePassRenderRequest* GetScenePassRequest(CameraFrameStage stage) const {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::ShadowCaster:
|
||||
return &shadowCaster;
|
||||
case CameraFrameStage::DepthOnly:
|
||||
return &depthOnly;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const ObjectIdRenderRequest* GetObjectIdRequest(CameraFrameStage stage) const {
|
||||
return stage == CameraFrameStage::ObjectId ? &objectId : nullptr;
|
||||
}
|
||||
|
||||
const RenderSurface& GetMainSceneSurface() const {
|
||||
if (postProcess.IsRequested()) {
|
||||
return postProcess.sourceSurface;
|
||||
}
|
||||
|
||||
if (finalOutput.IsRequested()) {
|
||||
return finalOutput.sourceSurface;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
const RenderSurface& GetFinalCompositedSurface() const {
|
||||
if (finalOutput.IsRequested()) {
|
||||
return finalOutput.destinationSurface;
|
||||
}
|
||||
|
||||
if (postProcess.IsRequested()) {
|
||||
return postProcess.destinationSurface;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
const RenderSurface* GetOutputSurface(CameraFrameStage stage) const {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PreScenePasses:
|
||||
case CameraFrameStage::MainScene:
|
||||
return &GetMainSceneSurface();
|
||||
case CameraFrameStage::ShadowCaster:
|
||||
return shadowCaster.IsRequested() ? &shadowCaster.surface : nullptr;
|
||||
case CameraFrameStage::DepthOnly:
|
||||
return depthOnly.IsRequested() ? &depthOnly.surface : nullptr;
|
||||
case CameraFrameStage::PostProcess:
|
||||
return postProcess.IsRequested() ? &postProcess.destinationSurface : nullptr;
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return finalOutput.IsRequested() ? &finalOutput.destinationSurface : nullptr;
|
||||
case CameraFrameStage::ObjectId:
|
||||
return objectId.IsRequested() ? &objectId.surface : nullptr;
|
||||
case CameraFrameStage::PostScenePasses:
|
||||
case CameraFrameStage::OverlayPasses:
|
||||
return &GetFinalCompositedSurface();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const RenderSurface* GetSourceSurface(CameraFrameStage stage) const {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PostProcess:
|
||||
return postProcess.IsRequested() ? &postProcess.sourceSurface : nullptr;
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return finalOutput.IsRequested() ? &finalOutput.sourceSurface : nullptr;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RHI::RHIResourceView* GetSourceColorView(CameraFrameStage stage) const {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PostProcess:
|
||||
return postProcess.IsRequested() ? postProcess.sourceColorView : nullptr;
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return finalOutput.IsRequested() ? finalOutput.sourceColorView : nullptr;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RHI::ResourceStates GetSourceColorState(CameraFrameStage stage) const {
|
||||
switch (stage) {
|
||||
case CameraFrameStage::PostProcess:
|
||||
return postProcess.IsRequested() ? postProcess.sourceColorState : RHI::ResourceStates::Common;
|
||||
case CameraFrameStage::FinalOutput:
|
||||
return finalOutput.IsRequested() ? finalOutput.sourceColorState : RHI::ResourceStates::Common;
|
||||
default:
|
||||
return RHI::ResourceStates::Common;
|
||||
}
|
||||
}
|
||||
|
||||
bool RequiresIntermediateSceneColor() const {
|
||||
return postProcess.IsRequested() || finalOutput.IsRequested();
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
return scene != nullptr &&
|
||||
camera != nullptr &&
|
||||
|
||||
@@ -107,7 +107,7 @@ std::vector<CameraRenderRequest> SceneRenderer::BuildRenderRequests(
|
||||
std::vector<CameraRenderRequest> requests = {};
|
||||
requests.reserve(plans.size());
|
||||
for (const CameraFramePlan& plan : plans) {
|
||||
requests.push_back(BuildLegacyCameraRenderRequest(plan));
|
||||
requests.push_back(BuildCompatibilityCameraRenderRequest(plan));
|
||||
}
|
||||
return requests;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ std::vector<CameraFramePlan> SceneRenderer::BuildFramePlans(
|
||||
const RenderSurface& surface) {
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
m_requestPlanner.BuildRequests(scene, overrideCamera, context, surface);
|
||||
std::vector<CameraFramePlan> plans = CreateFramePlansFromRequests(requests);
|
||||
std::vector<CameraFramePlan> plans = CreateFramePlansFromLegacyRequests(requests);
|
||||
ResolveCameraFinalColorPolicies(plans);
|
||||
AttachFullscreenStageRequests(context, plans);
|
||||
return plans;
|
||||
@@ -130,7 +130,7 @@ bool SceneRenderer::Render(const CameraRenderRequest& request) {
|
||||
}
|
||||
|
||||
bool SceneRenderer::Render(const std::vector<CameraRenderRequest>& requests) {
|
||||
std::vector<CameraFramePlan> plans = CreateFramePlansFromRequests(requests);
|
||||
std::vector<CameraFramePlan> plans = CreateFramePlansFromLegacyRequests(requests);
|
||||
return Render(plans);
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ bool SceneRenderer::Render(
|
||||
return Render(BuildFramePlans(scene, overrideCamera, context, surface));
|
||||
}
|
||||
|
||||
std::vector<CameraFramePlan> SceneRenderer::CreateFramePlansFromRequests(
|
||||
std::vector<CameraFramePlan> SceneRenderer::CreateFramePlansFromLegacyRequests(
|
||||
const std::vector<CameraRenderRequest>& requests) const {
|
||||
std::vector<CameraFramePlan> plans = {};
|
||||
plans.reserve(requests.size());
|
||||
|
||||
@@ -95,11 +95,6 @@ std::vector<CameraRenderRequest> SceneRenderRequestPlanner::BuildRequests(
|
||||
*mainDirectionalLight,
|
||||
effectiveShadowSettings,
|
||||
viewportAspect);
|
||||
if (request.directionalShadow.IsValid()) {
|
||||
request.shadowCaster.clearFlags = RenderClearFlags::Depth;
|
||||
request.shadowCaster.hasCameraDataOverride = true;
|
||||
request.shadowCaster.cameraDataOverride = request.directionalShadow.cameraData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -503,19 +503,19 @@ void AlphaCutoutSceneTest::RenderFrame() {
|
||||
renderContext.commandQueue = GetCommandQueue();
|
||||
renderContext.backendType = GetBackendType();
|
||||
|
||||
std::vector<CameraRenderRequest> requests =
|
||||
mSceneRenderer->BuildRenderRequests(*mScene, nullptr, renderContext, mainSurface);
|
||||
ASSERT_EQ(requests.size(), 1u);
|
||||
ASSERT_TRUE(requests[0].directionalShadow.IsValid());
|
||||
std::vector<CameraFramePlan> plans =
|
||||
mSceneRenderer->BuildFramePlans(*mScene, nullptr, renderContext, mainSurface);
|
||||
ASSERT_EQ(plans.size(), 1u);
|
||||
ASSERT_TRUE(plans[0].directionalShadow.IsValid());
|
||||
|
||||
RenderSurface depthOnlySurface(kFrameWidth, kFrameHeight);
|
||||
depthOnlySurface.SetDepthAttachment(mDepthView);
|
||||
depthOnlySurface.SetRenderArea(requests[0].surface.GetRenderArea());
|
||||
requests[0].depthOnly.surface = depthOnlySurface;
|
||||
requests[0].depthOnly.clearFlags = RenderClearFlags::Depth;
|
||||
requests[0].clearFlags = RenderClearFlags::Color;
|
||||
depthOnlySurface.SetRenderArea(plans[0].request.surface.GetRenderArea());
|
||||
plans[0].request.depthOnly.surface = depthOnlySurface;
|
||||
plans[0].request.depthOnly.clearFlags = RenderClearFlags::Depth;
|
||||
plans[0].request.clearFlags = RenderClearFlags::Color;
|
||||
|
||||
ASSERT_TRUE(mSceneRenderer->Render(requests));
|
||||
ASSERT_TRUE(mSceneRenderer->Render(plans));
|
||||
|
||||
commandList->Close();
|
||||
void* commandLists[] = { commandList };
|
||||
|
||||
@@ -449,10 +449,10 @@ void DirectionalShadowSceneTest::RenderSceneFrame() {
|
||||
renderContext.commandQueue = GetCommandQueue();
|
||||
renderContext.backendType = GetBackendType();
|
||||
|
||||
std::vector<CameraRenderRequest> requests =
|
||||
mSceneRenderer->BuildRenderRequests(*mScene, nullptr, renderContext, surface);
|
||||
ASSERT_FALSE(requests.empty());
|
||||
ASSERT_TRUE(mSceneRenderer->Render(requests));
|
||||
std::vector<CameraFramePlan> plans =
|
||||
mSceneRenderer->BuildFramePlans(*mScene, nullptr, renderContext, surface);
|
||||
ASSERT_FALSE(plans.empty());
|
||||
ASSERT_TRUE(mSceneRenderer->Render(plans));
|
||||
|
||||
Log("[TEST] DirectionalShadowSceneTest: closing command list");
|
||||
commandList->Close();
|
||||
|
||||
@@ -581,48 +581,49 @@ TEST(CameraRenderRequest_Test, ReportsFormalFrameStageContract) {
|
||||
EXPECT_STREQ(GetCameraFrameStageName(CameraFrameStage::FinalOutput), "FinalOutput");
|
||||
EXPECT_STREQ(GetCameraFrameStageName(CameraFrameStage::OverlayPasses), "OverlayPasses");
|
||||
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::PreScenePasses));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::ShadowCaster));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::DepthOnly));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::MainScene));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::PostProcess));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::FinalOutput));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::ObjectId));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::PostScenePasses));
|
||||
EXPECT_TRUE(request.HasFrameStage(CameraFrameStage::OverlayPasses));
|
||||
const CameraFramePlan plan = CameraFramePlan::FromRequest(request);
|
||||
EXPECT_TRUE(plan.HasFrameStage(CameraFrameStage::PreScenePasses));
|
||||
EXPECT_TRUE(plan.HasFrameStage(CameraFrameStage::ShadowCaster));
|
||||
EXPECT_TRUE(plan.HasFrameStage(CameraFrameStage::DepthOnly));
|
||||
EXPECT_TRUE(plan.HasFrameStage(CameraFrameStage::MainScene));
|
||||
EXPECT_TRUE(plan.HasFrameStage(CameraFrameStage::PostProcess));
|
||||
EXPECT_TRUE(plan.HasFrameStage(CameraFrameStage::FinalOutput));
|
||||
EXPECT_TRUE(plan.HasFrameStage(CameraFrameStage::ObjectId));
|
||||
EXPECT_TRUE(plan.HasFrameStage(CameraFrameStage::PostScenePasses));
|
||||
EXPECT_TRUE(plan.HasFrameStage(CameraFrameStage::OverlayPasses));
|
||||
|
||||
EXPECT_EQ(request.GetPassSequence(CameraFrameStage::PreScenePasses), &prePasses);
|
||||
EXPECT_EQ(request.GetPassSequence(CameraFrameStage::PostProcess), &postProcessPasses);
|
||||
EXPECT_EQ(request.GetPassSequence(CameraFrameStage::FinalOutput), &finalOutputPasses);
|
||||
EXPECT_EQ(request.GetPassSequence(CameraFrameStage::PostScenePasses), &postPasses);
|
||||
EXPECT_EQ(request.GetPassSequence(CameraFrameStage::OverlayPasses), &overlayPasses);
|
||||
EXPECT_EQ(request.GetScenePassRequest(CameraFrameStage::ShadowCaster), &request.shadowCaster);
|
||||
EXPECT_EQ(request.GetScenePassRequest(CameraFrameStage::DepthOnly), &request.depthOnly);
|
||||
EXPECT_EQ(request.GetScenePassRequest(CameraFrameStage::MainScene), nullptr);
|
||||
EXPECT_EQ(request.GetObjectIdRequest(CameraFrameStage::ObjectId), &request.objectId);
|
||||
EXPECT_EQ(request.GetObjectIdRequest(CameraFrameStage::MainScene), nullptr);
|
||||
EXPECT_TRUE(request.RequiresIntermediateSceneColor());
|
||||
EXPECT_EQ(request.GetMainSceneSurface().GetRenderAreaWidth(), 256u);
|
||||
EXPECT_EQ(request.GetMainSceneSurface().GetRenderAreaHeight(), 128u);
|
||||
EXPECT_EQ(request.GetFinalCompositedSurface().GetRenderAreaWidth(), 640u);
|
||||
EXPECT_EQ(request.GetFinalCompositedSurface().GetRenderAreaHeight(), 360u);
|
||||
ASSERT_NE(request.GetOutputSurface(CameraFrameStage::PostProcess), nullptr);
|
||||
EXPECT_EQ(request.GetOutputSurface(CameraFrameStage::PostProcess)->GetRenderAreaWidth(), 512u);
|
||||
ASSERT_NE(request.GetSourceSurface(CameraFrameStage::PostProcess), nullptr);
|
||||
EXPECT_EQ(request.GetSourceSurface(CameraFrameStage::PostProcess)->GetRenderAreaWidth(), 256u);
|
||||
EXPECT_EQ(plan.GetPassSequence(CameraFrameStage::PreScenePasses), &prePasses);
|
||||
EXPECT_EQ(plan.GetPassSequence(CameraFrameStage::PostProcess), &postProcessPasses);
|
||||
EXPECT_EQ(plan.GetPassSequence(CameraFrameStage::FinalOutput), &finalOutputPasses);
|
||||
EXPECT_EQ(plan.GetPassSequence(CameraFrameStage::PostScenePasses), &postPasses);
|
||||
EXPECT_EQ(plan.GetPassSequence(CameraFrameStage::OverlayPasses), &overlayPasses);
|
||||
EXPECT_EQ(plan.GetScenePassRequest(CameraFrameStage::ShadowCaster), &plan.shadowCaster);
|
||||
EXPECT_EQ(plan.GetScenePassRequest(CameraFrameStage::DepthOnly), &plan.request.depthOnly);
|
||||
EXPECT_EQ(plan.GetScenePassRequest(CameraFrameStage::MainScene), nullptr);
|
||||
EXPECT_EQ(plan.GetObjectIdRequest(CameraFrameStage::ObjectId), &plan.request.objectId);
|
||||
EXPECT_EQ(plan.GetObjectIdRequest(CameraFrameStage::MainScene), nullptr);
|
||||
EXPECT_TRUE(plan.RequiresIntermediateSceneColor());
|
||||
EXPECT_EQ(plan.GetMainSceneSurface().GetRenderAreaWidth(), 256u);
|
||||
EXPECT_EQ(plan.GetMainSceneSurface().GetRenderAreaHeight(), 128u);
|
||||
EXPECT_EQ(plan.GetFinalCompositedSurface().GetRenderAreaWidth(), 640u);
|
||||
EXPECT_EQ(plan.GetFinalCompositedSurface().GetRenderAreaHeight(), 360u);
|
||||
ASSERT_NE(plan.GetOutputSurface(CameraFrameStage::PostProcess), nullptr);
|
||||
EXPECT_EQ(plan.GetOutputSurface(CameraFrameStage::PostProcess)->GetRenderAreaWidth(), 512u);
|
||||
ASSERT_NE(plan.GetSourceSurface(CameraFrameStage::PostProcess), nullptr);
|
||||
EXPECT_EQ(plan.GetSourceSurface(CameraFrameStage::PostProcess)->GetRenderAreaWidth(), 256u);
|
||||
EXPECT_EQ(
|
||||
request.GetSourceColorView(CameraFrameStage::PostProcess),
|
||||
plan.GetSourceColorView(CameraFrameStage::PostProcess),
|
||||
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(20));
|
||||
EXPECT_EQ(
|
||||
request.GetSourceColorState(CameraFrameStage::PostProcess),
|
||||
plan.GetSourceColorState(CameraFrameStage::PostProcess),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
ASSERT_NE(request.GetSourceSurface(CameraFrameStage::FinalOutput), nullptr);
|
||||
EXPECT_EQ(request.GetSourceSurface(CameraFrameStage::FinalOutput)->GetRenderAreaWidth(), 512u);
|
||||
ASSERT_NE(plan.GetSourceSurface(CameraFrameStage::FinalOutput), nullptr);
|
||||
EXPECT_EQ(plan.GetSourceSurface(CameraFrameStage::FinalOutput)->GetRenderAreaWidth(), 512u);
|
||||
EXPECT_EQ(
|
||||
request.GetSourceColorView(CameraFrameStage::FinalOutput),
|
||||
plan.GetSourceColorView(CameraFrameStage::FinalOutput),
|
||||
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(40));
|
||||
EXPECT_EQ(
|
||||
request.GetSourceColorState(CameraFrameStage::FinalOutput),
|
||||
plan.GetSourceColorState(CameraFrameStage::FinalOutput),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
}
|
||||
|
||||
@@ -1723,23 +1724,23 @@ TEST(SceneRenderer_Test, BuildsSortedRequestsForAllUsableCamerasAndHonorsOverrid
|
||||
const RenderContext context = CreateValidContext();
|
||||
const RenderSurface surface(320, 180);
|
||||
|
||||
const std::vector<CameraRenderRequest> defaultRequests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, context, surface);
|
||||
ASSERT_EQ(defaultRequests.size(), 2u);
|
||||
EXPECT_EQ(defaultRequests[0].camera, lowCamera);
|
||||
EXPECT_EQ(defaultRequests[0].cameraDepth, 1.0f);
|
||||
EXPECT_EQ(defaultRequests[0].clearFlags, RenderClearFlags::All);
|
||||
EXPECT_EQ(defaultRequests[0].surface.GetWidth(), 320u);
|
||||
EXPECT_EQ(defaultRequests[0].surface.GetHeight(), 180u);
|
||||
EXPECT_EQ(defaultRequests[1].camera, highCamera);
|
||||
EXPECT_EQ(defaultRequests[1].cameraDepth, 5.0f);
|
||||
EXPECT_EQ(defaultRequests[1].clearFlags, RenderClearFlags::None);
|
||||
const std::vector<CameraFramePlan> defaultPlans =
|
||||
renderer.BuildFramePlans(scene, nullptr, context, surface);
|
||||
ASSERT_EQ(defaultPlans.size(), 2u);
|
||||
EXPECT_EQ(defaultPlans[0].request.camera, lowCamera);
|
||||
EXPECT_EQ(defaultPlans[0].request.cameraDepth, 1.0f);
|
||||
EXPECT_EQ(defaultPlans[0].request.clearFlags, RenderClearFlags::All);
|
||||
EXPECT_EQ(defaultPlans[0].request.surface.GetWidth(), 320u);
|
||||
EXPECT_EQ(defaultPlans[0].request.surface.GetHeight(), 180u);
|
||||
EXPECT_EQ(defaultPlans[1].request.camera, highCamera);
|
||||
EXPECT_EQ(defaultPlans[1].request.cameraDepth, 5.0f);
|
||||
EXPECT_EQ(defaultPlans[1].request.clearFlags, RenderClearFlags::None);
|
||||
|
||||
const std::vector<CameraRenderRequest> overrideRequests =
|
||||
renderer.BuildRenderRequests(scene, lowCamera, context, surface);
|
||||
ASSERT_EQ(overrideRequests.size(), 1u);
|
||||
EXPECT_EQ(overrideRequests[0].camera, lowCamera);
|
||||
EXPECT_EQ(overrideRequests[0].clearFlags, RenderClearFlags::All);
|
||||
const std::vector<CameraFramePlan> overridePlans =
|
||||
renderer.BuildFramePlans(scene, lowCamera, context, surface);
|
||||
ASSERT_EQ(overridePlans.size(), 1u);
|
||||
EXPECT_EQ(overridePlans[0].request.camera, lowCamera);
|
||||
EXPECT_EQ(overridePlans[0].request.clearFlags, RenderClearFlags::All);
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, RendersBaseCamerasBeforeOverlayCamerasAndResolvesAutoClearPerStackType) {
|
||||
@@ -1761,19 +1762,19 @@ TEST(SceneRenderer_Test, RendersBaseCamerasBeforeOverlayCamerasAndResolvesAutoCl
|
||||
overlayCamera->SetStackType(CameraStackType::Overlay);
|
||||
|
||||
SceneRenderer renderer;
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), RenderSurface(640, 360));
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, nullptr, CreateValidContext(), RenderSurface(640, 360));
|
||||
|
||||
ASSERT_EQ(requests.size(), 3u);
|
||||
EXPECT_EQ(requests[0].camera, earlyBaseCamera);
|
||||
EXPECT_EQ(requests[0].cameraStackOrder, 0u);
|
||||
EXPECT_EQ(requests[0].clearFlags, RenderClearFlags::All);
|
||||
EXPECT_EQ(requests[1].camera, lateBaseCamera);
|
||||
EXPECT_EQ(requests[1].cameraStackOrder, 0u);
|
||||
EXPECT_EQ(requests[1].clearFlags, RenderClearFlags::Depth);
|
||||
EXPECT_EQ(requests[2].camera, overlayCamera);
|
||||
EXPECT_EQ(requests[2].cameraStackOrder, 1u);
|
||||
EXPECT_EQ(requests[2].clearFlags, RenderClearFlags::Depth);
|
||||
ASSERT_EQ(plans.size(), 3u);
|
||||
EXPECT_EQ(plans[0].request.camera, earlyBaseCamera);
|
||||
EXPECT_EQ(plans[0].request.cameraStackOrder, 0u);
|
||||
EXPECT_EQ(plans[0].request.clearFlags, RenderClearFlags::All);
|
||||
EXPECT_EQ(plans[1].request.camera, lateBaseCamera);
|
||||
EXPECT_EQ(plans[1].request.cameraStackOrder, 0u);
|
||||
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.clearFlags, RenderClearFlags::Depth);
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, PreservesSceneTraversalOrderForEqualPriorityCameras) {
|
||||
@@ -1792,12 +1793,12 @@ TEST(SceneRenderer_Test, PreservesSceneTraversalOrderForEqualPriorityCameras) {
|
||||
secondCamera->SetStackType(CameraStackType::Base);
|
||||
|
||||
SceneRenderer renderer;
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), RenderSurface(640, 360));
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, nullptr, CreateValidContext(), RenderSurface(640, 360));
|
||||
|
||||
ASSERT_EQ(requests.size(), 2u);
|
||||
EXPECT_EQ(requests[0].camera, firstCamera);
|
||||
EXPECT_EQ(requests[1].camera, secondCamera);
|
||||
ASSERT_EQ(plans.size(), 2u);
|
||||
EXPECT_EQ(plans[0].request.camera, firstCamera);
|
||||
EXPECT_EQ(plans[1].request.camera, secondCamera);
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, FallsBackToColorClearForFirstOverlayCameraWhenNoBaseCameraExists) {
|
||||
@@ -1814,14 +1815,14 @@ TEST(SceneRenderer_Test, FallsBackToColorClearForFirstOverlayCameraWhenNoBaseCam
|
||||
secondOverlay->SetStackType(CameraStackType::Overlay);
|
||||
|
||||
SceneRenderer renderer;
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), RenderSurface(320, 180));
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, nullptr, CreateValidContext(), RenderSurface(320, 180));
|
||||
|
||||
ASSERT_EQ(requests.size(), 2u);
|
||||
EXPECT_EQ(requests[0].camera, firstOverlay);
|
||||
EXPECT_EQ(requests[0].clearFlags, RenderClearFlags::All);
|
||||
EXPECT_EQ(requests[1].camera, secondOverlay);
|
||||
EXPECT_EQ(requests[1].clearFlags, RenderClearFlags::Depth);
|
||||
ASSERT_EQ(plans.size(), 2u);
|
||||
EXPECT_EQ(plans[0].request.camera, firstOverlay);
|
||||
EXPECT_EQ(plans[0].request.clearFlags, RenderClearFlags::All);
|
||||
EXPECT_EQ(plans[1].request.camera, secondOverlay);
|
||||
EXPECT_EQ(plans[1].request.clearFlags, RenderClearFlags::Depth);
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, HonorsExplicitOverrideCameraClearMode) {
|
||||
@@ -1834,12 +1835,12 @@ TEST(SceneRenderer_Test, HonorsExplicitOverrideCameraClearMode) {
|
||||
camera->SetClearMode(CameraClearMode::DepthOnly);
|
||||
|
||||
SceneRenderer renderer;
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, camera, CreateValidContext(), RenderSurface(640, 360));
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, camera, CreateValidContext(), RenderSurface(640, 360));
|
||||
|
||||
ASSERT_EQ(requests.size(), 1u);
|
||||
EXPECT_EQ(requests[0].camera, camera);
|
||||
EXPECT_EQ(requests[0].clearFlags, RenderClearFlags::Depth);
|
||||
ASSERT_EQ(plans.size(), 1u);
|
||||
EXPECT_EQ(plans[0].request.camera, camera);
|
||||
EXPECT_EQ(plans[0].request.clearFlags, RenderClearFlags::Depth);
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, ResolvesNormalizedCameraViewportRectToPerRequestRenderArea) {
|
||||
@@ -1852,11 +1853,11 @@ TEST(SceneRenderer_Test, ResolvesNormalizedCameraViewportRectToPerRequestRenderA
|
||||
camera->SetViewportRect(XCEngine::Math::Rect(0.25f, 0.1f, 0.5f, 0.4f));
|
||||
|
||||
SceneRenderer renderer;
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), RenderSurface(800, 600));
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, nullptr, CreateValidContext(), RenderSurface(800, 600));
|
||||
|
||||
ASSERT_EQ(requests.size(), 1u);
|
||||
const XCEngine::Math::RectInt renderArea = requests[0].surface.GetRenderArea();
|
||||
ASSERT_EQ(plans.size(), 1u);
|
||||
const XCEngine::Math::RectInt renderArea = plans[0].request.surface.GetRenderArea();
|
||||
EXPECT_EQ(renderArea.x, 200);
|
||||
EXPECT_EQ(renderArea.y, 60);
|
||||
EXPECT_EQ(renderArea.width, 400);
|
||||
@@ -1876,11 +1877,11 @@ TEST(SceneRenderer_Test, ComposesCameraViewportRectWithinExistingSurfaceRenderAr
|
||||
surface.SetRenderArea(XCEngine::Math::RectInt(100, 50, 400, 300));
|
||||
|
||||
SceneRenderer renderer;
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), surface);
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, nullptr, CreateValidContext(), surface);
|
||||
|
||||
ASSERT_EQ(requests.size(), 1u);
|
||||
const XCEngine::Math::RectInt renderArea = requests[0].surface.GetRenderArea();
|
||||
ASSERT_EQ(plans.size(), 1u);
|
||||
const XCEngine::Math::RectInt renderArea = plans[0].request.surface.GetRenderArea();
|
||||
EXPECT_EQ(renderArea.x, 200);
|
||||
EXPECT_EQ(renderArea.y, 80);
|
||||
EXPECT_EQ(renderArea.width, 200);
|
||||
@@ -1900,11 +1901,11 @@ TEST(SceneRenderer_Test, PreservesExistingSurfaceRenderAreaForFullViewportCamera
|
||||
surface.SetRenderArea(XCEngine::Math::RectInt(80, 120, 320, 240));
|
||||
|
||||
SceneRenderer renderer;
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), surface);
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, nullptr, CreateValidContext(), surface);
|
||||
|
||||
ASSERT_EQ(requests.size(), 1u);
|
||||
const XCEngine::Math::RectInt renderArea = requests[0].surface.GetRenderArea();
|
||||
ASSERT_EQ(plans.size(), 1u);
|
||||
const XCEngine::Math::RectInt renderArea = plans[0].request.surface.GetRenderArea();
|
||||
EXPECT_EQ(renderArea.x, 80);
|
||||
EXPECT_EQ(renderArea.y, 120);
|
||||
EXPECT_EQ(renderArea.width, 320);
|
||||
@@ -1950,44 +1951,44 @@ TEST(SceneRenderer_Test, BuildsCameraColorScalePostProcessRequestFromCameraPassS
|
||||
surface.SetDepthStateAfter(XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
|
||||
SceneRenderer renderer;
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, context, surface);
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, nullptr, context, surface);
|
||||
|
||||
ASSERT_EQ(requests.size(), 1u);
|
||||
const CameraRenderRequest& request = requests[0];
|
||||
EXPECT_TRUE(request.postProcess.IsRequested());
|
||||
EXPECT_TRUE(request.postProcess.IsValid());
|
||||
EXPECT_NE(request.postProcess.passes, nullptr);
|
||||
ASSERT_EQ(request.postProcess.passes->GetPassCount(), 2u);
|
||||
EXPECT_EQ(request.postProcess.destinationSurface.GetColorAttachments()[0], backBufferColorView);
|
||||
EXPECT_EQ(request.postProcess.destinationSurface.GetDepthAttachment(), depthView);
|
||||
ASSERT_EQ(plans.size(), 1u);
|
||||
const CameraFramePlan& plan = plans[0];
|
||||
EXPECT_TRUE(plan.postProcess.IsRequested());
|
||||
EXPECT_TRUE(plan.postProcess.IsValid());
|
||||
EXPECT_NE(plan.postProcess.passes, nullptr);
|
||||
ASSERT_EQ(plan.postProcess.passes->GetPassCount(), 2u);
|
||||
EXPECT_EQ(plan.postProcess.destinationSurface.GetColorAttachments()[0], backBufferColorView);
|
||||
EXPECT_EQ(plan.postProcess.destinationSurface.GetDepthAttachment(), depthView);
|
||||
EXPECT_EQ(
|
||||
request.postProcess.destinationSurface.GetDepthStateBefore(),
|
||||
plan.postProcess.destinationSurface.GetDepthStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::Common);
|
||||
EXPECT_EQ(
|
||||
request.postProcess.destinationSurface.GetDepthStateAfter(),
|
||||
plan.postProcess.destinationSurface.GetDepthStateAfter(),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
EXPECT_EQ(request.postProcess.sourceSurface.GetDepthAttachment(), depthView);
|
||||
EXPECT_EQ(plan.postProcess.sourceSurface.GetDepthAttachment(), depthView);
|
||||
EXPECT_EQ(
|
||||
request.postProcess.sourceSurface.GetDepthStateBefore(),
|
||||
plan.postProcess.sourceSurface.GetDepthStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::Common);
|
||||
EXPECT_EQ(
|
||||
request.postProcess.sourceSurface.GetDepthStateAfter(),
|
||||
plan.postProcess.sourceSurface.GetDepthStateAfter(),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
EXPECT_EQ(request.postProcess.sourceSurface.GetWidth(), 800u);
|
||||
EXPECT_EQ(request.postProcess.sourceSurface.GetHeight(), 600u);
|
||||
const XCEngine::Math::RectInt sourceRenderArea = request.postProcess.sourceSurface.GetRenderArea();
|
||||
EXPECT_EQ(plan.postProcess.sourceSurface.GetWidth(), 800u);
|
||||
EXPECT_EQ(plan.postProcess.sourceSurface.GetHeight(), 600u);
|
||||
const XCEngine::Math::RectInt sourceRenderArea = plan.postProcess.sourceSurface.GetRenderArea();
|
||||
EXPECT_EQ(sourceRenderArea.x, 200);
|
||||
EXPECT_EQ(sourceRenderArea.y, 75);
|
||||
EXPECT_EQ(sourceRenderArea.width, 400);
|
||||
EXPECT_EQ(sourceRenderArea.height, 375);
|
||||
EXPECT_NE(request.postProcess.sourceColorView, nullptr);
|
||||
EXPECT_NE(request.postProcess.sourceColorView, backBufferColorView);
|
||||
EXPECT_NE(plan.postProcess.sourceColorView, nullptr);
|
||||
EXPECT_NE(plan.postProcess.sourceColorView, backBufferColorView);
|
||||
EXPECT_EQ(
|
||||
request.postProcess.sourceColorState,
|
||||
plan.postProcess.sourceColorState,
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
ASSERT_FALSE(request.postProcess.sourceSurface.GetColorAttachments().empty());
|
||||
EXPECT_NE(request.postProcess.sourceSurface.GetColorAttachments()[0], backBufferColorView);
|
||||
ASSERT_FALSE(plan.postProcess.sourceSurface.GetColorAttachments().empty());
|
||||
EXPECT_NE(plan.postProcess.sourceSurface.GetColorAttachments()[0], backBufferColorView);
|
||||
EXPECT_EQ(allocationState->createTextureCalls, 1);
|
||||
EXPECT_EQ(allocationState->createRenderTargetViewCalls, 1);
|
||||
EXPECT_EQ(allocationState->createShaderViewCalls, 1);
|
||||
@@ -2021,22 +2022,22 @@ TEST(SceneRenderer_Test, ResolvesFinalColorPolicyFromPipelineDefaultsAndCameraOv
|
||||
XCEngine::Math::Vector4(1.0f, 0.95f, 0.9f, 1.0f);
|
||||
|
||||
SceneRenderer renderer(std::make_shared<MockPipelineAsset>(assetState));
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, CreateValidContext(), RenderSurface(640, 360));
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, nullptr, CreateValidContext(), RenderSurface(640, 360));
|
||||
|
||||
ASSERT_EQ(requests.size(), 1u);
|
||||
const CameraRenderRequest& request = requests[0];
|
||||
EXPECT_TRUE(request.finalColorPolicy.hasPipelineDefaults);
|
||||
EXPECT_TRUE(request.finalColorPolicy.hasCameraOverrides);
|
||||
ASSERT_EQ(plans.size(), 1u);
|
||||
const CameraFramePlan& plan = plans[0];
|
||||
EXPECT_TRUE(plan.finalColorPolicy.hasPipelineDefaults);
|
||||
EXPECT_TRUE(plan.finalColorPolicy.hasCameraOverrides);
|
||||
EXPECT_EQ(
|
||||
request.finalColorPolicy.outputTransferMode,
|
||||
plan.finalColorPolicy.outputTransferMode,
|
||||
FinalColorOutputTransferMode::LinearToSRGB);
|
||||
EXPECT_EQ(
|
||||
request.finalColorPolicy.exposureMode,
|
||||
plan.finalColorPolicy.exposureMode,
|
||||
FinalColorExposureMode::Fixed);
|
||||
EXPECT_FLOAT_EQ(request.finalColorPolicy.exposureValue, 1.8f);
|
||||
EXPECT_FLOAT_EQ(request.finalColorPolicy.finalColorScale.x, 0.95f);
|
||||
EXPECT_FALSE(request.finalOutput.IsRequested());
|
||||
EXPECT_FLOAT_EQ(plan.finalColorPolicy.exposureValue, 1.8f);
|
||||
EXPECT_FLOAT_EQ(plan.finalColorPolicy.finalColorScale.x, 0.95f);
|
||||
EXPECT_FALSE(plan.finalOutput.IsRequested());
|
||||
}
|
||||
|
||||
TEST(SceneRenderer_Test, BuildsFinalOutputRequestFromResolvedFinalColorPolicy) {
|
||||
@@ -2084,40 +2085,40 @@ TEST(SceneRenderer_Test, BuildsFinalOutputRequestFromResolvedFinalColorPolicy) {
|
||||
surface.SetDepthStateAfter(XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
|
||||
SceneRenderer renderer(std::make_shared<MockPipelineAsset>(assetState));
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, context, surface);
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, nullptr, context, surface);
|
||||
|
||||
ASSERT_EQ(requests.size(), 1u);
|
||||
const CameraRenderRequest& request = requests[0];
|
||||
EXPECT_FALSE(request.postProcess.IsRequested());
|
||||
EXPECT_TRUE(request.finalOutput.IsRequested());
|
||||
EXPECT_TRUE(request.finalOutput.IsValid());
|
||||
ASSERT_NE(request.finalOutput.passes, nullptr);
|
||||
EXPECT_EQ(request.finalOutput.passes->GetPassCount(), 1u);
|
||||
EXPECT_EQ(request.finalOutput.destinationSurface.GetColorAttachments()[0], backBufferColorView);
|
||||
EXPECT_EQ(request.finalOutput.destinationSurface.GetDepthAttachment(), depthView);
|
||||
ASSERT_EQ(plans.size(), 1u);
|
||||
const CameraFramePlan& plan = plans[0];
|
||||
EXPECT_FALSE(plan.postProcess.IsRequested());
|
||||
EXPECT_TRUE(plan.finalOutput.IsRequested());
|
||||
EXPECT_TRUE(plan.finalOutput.IsValid());
|
||||
ASSERT_NE(plan.finalOutput.passes, nullptr);
|
||||
EXPECT_EQ(plan.finalOutput.passes->GetPassCount(), 1u);
|
||||
EXPECT_EQ(plan.finalOutput.destinationSurface.GetColorAttachments()[0], backBufferColorView);
|
||||
EXPECT_EQ(plan.finalOutput.destinationSurface.GetDepthAttachment(), depthView);
|
||||
EXPECT_EQ(
|
||||
request.finalOutput.destinationSurface.GetDepthStateBefore(),
|
||||
plan.finalOutput.destinationSurface.GetDepthStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::Common);
|
||||
EXPECT_EQ(
|
||||
request.finalOutput.destinationSurface.GetDepthStateAfter(),
|
||||
plan.finalOutput.destinationSurface.GetDepthStateAfter(),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
EXPECT_EQ(request.finalOutput.sourceSurface.GetDepthAttachment(), depthView);
|
||||
EXPECT_EQ(plan.finalOutput.sourceSurface.GetDepthAttachment(), depthView);
|
||||
EXPECT_EQ(
|
||||
request.finalOutput.sourceSurface.GetDepthStateBefore(),
|
||||
plan.finalOutput.sourceSurface.GetDepthStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::Common);
|
||||
EXPECT_EQ(
|
||||
request.finalOutput.sourceSurface.GetDepthStateAfter(),
|
||||
plan.finalOutput.sourceSurface.GetDepthStateAfter(),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
EXPECT_EQ(request.finalOutput.sourceSurface.GetWidth(), 800u);
|
||||
EXPECT_EQ(request.finalOutput.sourceSurface.GetHeight(), 600u);
|
||||
EXPECT_NE(request.finalOutput.sourceColorView, nullptr);
|
||||
EXPECT_NE(request.finalOutput.sourceColorView, backBufferColorView);
|
||||
EXPECT_EQ(plan.finalOutput.sourceSurface.GetWidth(), 800u);
|
||||
EXPECT_EQ(plan.finalOutput.sourceSurface.GetHeight(), 600u);
|
||||
EXPECT_NE(plan.finalOutput.sourceColorView, nullptr);
|
||||
EXPECT_NE(plan.finalOutput.sourceColorView, backBufferColorView);
|
||||
EXPECT_EQ(
|
||||
request.finalOutput.sourceColorState,
|
||||
plan.finalOutput.sourceColorState,
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
ASSERT_FALSE(request.finalOutput.sourceSurface.GetColorAttachments().empty());
|
||||
EXPECT_NE(request.finalOutput.sourceSurface.GetColorAttachments()[0], backBufferColorView);
|
||||
ASSERT_FALSE(plan.finalOutput.sourceSurface.GetColorAttachments().empty());
|
||||
EXPECT_NE(plan.finalOutput.sourceSurface.GetColorAttachments()[0], backBufferColorView);
|
||||
EXPECT_EQ(allocationState->createTextureCalls, 1);
|
||||
EXPECT_EQ(allocationState->createRenderTargetViewCalls, 1);
|
||||
EXPECT_EQ(allocationState->createShaderViewCalls, 1);
|
||||
@@ -2170,66 +2171,66 @@ TEST(SceneRenderer_Test, RoutesPostProcessIntoIntermediateSurfaceBeforeFinalOutp
|
||||
surface.SetDepthStateAfter(XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
|
||||
SceneRenderer renderer;
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, context, surface);
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, nullptr, context, surface);
|
||||
|
||||
ASSERT_EQ(requests.size(), 1u);
|
||||
const CameraRenderRequest& request = requests[0];
|
||||
EXPECT_TRUE(request.postProcess.IsRequested());
|
||||
EXPECT_TRUE(request.finalOutput.IsRequested());
|
||||
ASSERT_NE(request.postProcess.passes, nullptr);
|
||||
ASSERT_NE(request.finalOutput.passes, nullptr);
|
||||
EXPECT_EQ(request.postProcess.passes->GetPassCount(), 1u);
|
||||
EXPECT_EQ(request.finalOutput.passes->GetPassCount(), 1u);
|
||||
ASSERT_EQ(plans.size(), 1u);
|
||||
const CameraFramePlan& plan = plans[0];
|
||||
EXPECT_TRUE(plan.postProcess.IsRequested());
|
||||
EXPECT_TRUE(plan.finalOutput.IsRequested());
|
||||
ASSERT_NE(plan.postProcess.passes, nullptr);
|
||||
ASSERT_NE(plan.finalOutput.passes, nullptr);
|
||||
EXPECT_EQ(plan.postProcess.passes->GetPassCount(), 1u);
|
||||
EXPECT_EQ(plan.finalOutput.passes->GetPassCount(), 1u);
|
||||
|
||||
ASSERT_FALSE(request.postProcess.sourceSurface.GetColorAttachments().empty());
|
||||
ASSERT_FALSE(request.postProcess.destinationSurface.GetColorAttachments().empty());
|
||||
ASSERT_FALSE(request.finalOutput.sourceSurface.GetColorAttachments().empty());
|
||||
ASSERT_FALSE(plan.postProcess.sourceSurface.GetColorAttachments().empty());
|
||||
ASSERT_FALSE(plan.postProcess.destinationSurface.GetColorAttachments().empty());
|
||||
ASSERT_FALSE(plan.finalOutput.sourceSurface.GetColorAttachments().empty());
|
||||
|
||||
EXPECT_NE(
|
||||
request.postProcess.sourceSurface.GetColorAttachments()[0],
|
||||
request.postProcess.destinationSurface.GetColorAttachments()[0]);
|
||||
plan.postProcess.sourceSurface.GetColorAttachments()[0],
|
||||
plan.postProcess.destinationSurface.GetColorAttachments()[0]);
|
||||
EXPECT_EQ(
|
||||
request.finalOutput.sourceSurface.GetColorAttachments()[0],
|
||||
request.postProcess.destinationSurface.GetColorAttachments()[0]);
|
||||
EXPECT_EQ(request.finalOutput.destinationSurface.GetColorAttachments()[0], backBufferColorView);
|
||||
plan.finalOutput.sourceSurface.GetColorAttachments()[0],
|
||||
plan.postProcess.destinationSurface.GetColorAttachments()[0]);
|
||||
EXPECT_EQ(plan.finalOutput.destinationSurface.GetColorAttachments()[0], backBufferColorView);
|
||||
|
||||
EXPECT_EQ(request.postProcess.sourceSurface.GetDepthAttachment(), depthView);
|
||||
EXPECT_EQ(plan.postProcess.sourceSurface.GetDepthAttachment(), depthView);
|
||||
EXPECT_EQ(
|
||||
request.postProcess.sourceSurface.GetDepthStateBefore(),
|
||||
plan.postProcess.sourceSurface.GetDepthStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::Common);
|
||||
EXPECT_EQ(
|
||||
request.postProcess.sourceSurface.GetDepthStateAfter(),
|
||||
plan.postProcess.sourceSurface.GetDepthStateAfter(),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
EXPECT_EQ(request.postProcess.destinationSurface.GetDepthAttachment(), nullptr);
|
||||
EXPECT_EQ(request.finalOutput.sourceSurface.GetDepthAttachment(), nullptr);
|
||||
EXPECT_EQ(request.finalOutput.destinationSurface.GetDepthAttachment(), depthView);
|
||||
EXPECT_EQ(plan.postProcess.destinationSurface.GetDepthAttachment(), nullptr);
|
||||
EXPECT_EQ(plan.finalOutput.sourceSurface.GetDepthAttachment(), nullptr);
|
||||
EXPECT_EQ(plan.finalOutput.destinationSurface.GetDepthAttachment(), depthView);
|
||||
EXPECT_EQ(
|
||||
request.finalOutput.destinationSurface.GetDepthStateBefore(),
|
||||
plan.finalOutput.destinationSurface.GetDepthStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::Common);
|
||||
EXPECT_EQ(
|
||||
request.finalOutput.destinationSurface.GetDepthStateAfter(),
|
||||
plan.finalOutput.destinationSurface.GetDepthStateAfter(),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
|
||||
const XCEngine::Math::RectInt postProcessSourceArea = request.postProcess.sourceSurface.GetRenderArea();
|
||||
const XCEngine::Math::RectInt postProcessSourceArea = plan.postProcess.sourceSurface.GetRenderArea();
|
||||
EXPECT_EQ(postProcessSourceArea.x, 200);
|
||||
EXPECT_EQ(postProcessSourceArea.y, 75);
|
||||
EXPECT_EQ(postProcessSourceArea.width, 400);
|
||||
EXPECT_EQ(postProcessSourceArea.height, 375);
|
||||
const XCEngine::Math::RectInt finalOutputSourceArea = request.finalOutput.sourceSurface.GetRenderArea();
|
||||
const XCEngine::Math::RectInt finalOutputSourceArea = plan.finalOutput.sourceSurface.GetRenderArea();
|
||||
EXPECT_EQ(finalOutputSourceArea.x, 200);
|
||||
EXPECT_EQ(finalOutputSourceArea.y, 75);
|
||||
EXPECT_EQ(finalOutputSourceArea.width, 400);
|
||||
EXPECT_EQ(finalOutputSourceArea.height, 375);
|
||||
|
||||
EXPECT_NE(request.postProcess.sourceColorView, nullptr);
|
||||
EXPECT_NE(request.finalOutput.sourceColorView, nullptr);
|
||||
EXPECT_NE(request.postProcess.sourceColorView, request.finalOutput.sourceColorView);
|
||||
EXPECT_NE(plan.postProcess.sourceColorView, nullptr);
|
||||
EXPECT_NE(plan.finalOutput.sourceColorView, nullptr);
|
||||
EXPECT_NE(plan.postProcess.sourceColorView, plan.finalOutput.sourceColorView);
|
||||
EXPECT_EQ(
|
||||
request.postProcess.sourceColorState,
|
||||
plan.postProcess.sourceColorState,
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
EXPECT_EQ(
|
||||
request.finalOutput.sourceColorState,
|
||||
plan.finalOutput.sourceColorState,
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
EXPECT_EQ(allocationState->createTextureCalls, 2);
|
||||
EXPECT_EQ(allocationState->createRenderTargetViewCalls, 2);
|
||||
@@ -2279,14 +2280,14 @@ TEST(SceneRenderer_Test, DoesNotBuildFullscreenStagesForMultisampledMainSceneSur
|
||||
surface.SetSampleDesc(4u, 0u);
|
||||
|
||||
SceneRenderer renderer;
|
||||
const std::vector<CameraRenderRequest> requests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, context, surface);
|
||||
const std::vector<CameraFramePlan> plans =
|
||||
renderer.BuildFramePlans(scene, nullptr, context, surface);
|
||||
|
||||
ASSERT_EQ(requests.size(), 1u);
|
||||
const CameraRenderRequest& request = requests[0];
|
||||
EXPECT_EQ(request.surface.GetSampleCount(), 4u);
|
||||
EXPECT_FALSE(request.postProcess.IsRequested());
|
||||
EXPECT_FALSE(request.finalOutput.IsRequested());
|
||||
ASSERT_EQ(plans.size(), 1u);
|
||||
const CameraFramePlan& plan = plans[0];
|
||||
EXPECT_EQ(plan.request.surface.GetSampleCount(), 4u);
|
||||
EXPECT_FALSE(plan.postProcess.IsRequested());
|
||||
EXPECT_FALSE(plan.finalOutput.IsRequested());
|
||||
EXPECT_EQ(allocationState->createTextureCalls, 0);
|
||||
EXPECT_EQ(allocationState->createRenderTargetViewCalls, 0);
|
||||
EXPECT_EQ(allocationState->createShaderViewCalls, 0);
|
||||
@@ -2331,27 +2332,29 @@ TEST(SceneRenderer_Test, ReusesTrackedSceneColorStateAcrossFramesWhenPostProcess
|
||||
|
||||
SceneRenderer renderer(std::make_unique<MockPipeline>(pipelineState));
|
||||
|
||||
std::vector<CameraRenderRequest> firstFrameRequests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, context, surface);
|
||||
ASSERT_EQ(firstFrameRequests.size(), 1u);
|
||||
std::vector<CameraFramePlan> firstFramePlans =
|
||||
renderer.BuildFramePlans(scene, nullptr, context, surface);
|
||||
ASSERT_EQ(firstFramePlans.size(), 1u);
|
||||
CameraFramePlan firstFramePlan = firstFramePlans[0];
|
||||
EXPECT_EQ(
|
||||
firstFrameRequests[0].GetMainSceneSurface().GetColorStateBefore(),
|
||||
firstFramePlan.GetMainSceneSurface().GetColorStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::Common);
|
||||
|
||||
RenderPassSequence postProcessPasses;
|
||||
postProcessPasses.AddPass(std::make_unique<MockScenePass>(pipelineState, "postProcess"));
|
||||
firstFrameRequests[0].postProcess.passes = &postProcessPasses;
|
||||
firstFramePlans[0].postProcess.passes = &postProcessPasses;
|
||||
|
||||
ASSERT_TRUE(renderer.Render(firstFrameRequests));
|
||||
ASSERT_TRUE(renderer.Render(firstFramePlans));
|
||||
|
||||
const std::vector<CameraRenderRequest> secondFrameRequests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, context, surface);
|
||||
ASSERT_EQ(secondFrameRequests.size(), 1u);
|
||||
const std::vector<CameraFramePlan> secondFramePlans =
|
||||
renderer.BuildFramePlans(scene, nullptr, context, surface);
|
||||
ASSERT_EQ(secondFramePlans.size(), 1u);
|
||||
const CameraFramePlan secondFramePlan = secondFramePlans[0];
|
||||
EXPECT_EQ(
|
||||
secondFrameRequests[0].GetMainSceneSurface().GetColorStateBefore(),
|
||||
secondFramePlan.GetMainSceneSurface().GetColorStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
EXPECT_EQ(
|
||||
secondFrameRequests[0].GetMainSceneSurface().GetColorStateAfter(),
|
||||
secondFramePlan.GetMainSceneSurface().GetColorStateAfter(),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
|
||||
delete depthView;
|
||||
@@ -2399,38 +2402,40 @@ TEST(SceneRenderer_Test, ReusesTrackedPostProcessOutputStateAcrossFramesWhenFina
|
||||
|
||||
SceneRenderer renderer(std::make_unique<MockPipeline>(pipelineState));
|
||||
|
||||
std::vector<CameraRenderRequest> firstFrameRequests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, context, surface);
|
||||
ASSERT_EQ(firstFrameRequests.size(), 1u);
|
||||
EXPECT_TRUE(firstFrameRequests[0].postProcess.IsRequested());
|
||||
EXPECT_TRUE(firstFrameRequests[0].finalOutput.IsRequested());
|
||||
std::vector<CameraFramePlan> firstFramePlans =
|
||||
renderer.BuildFramePlans(scene, nullptr, context, surface);
|
||||
ASSERT_EQ(firstFramePlans.size(), 1u);
|
||||
CameraFramePlan firstFramePlan = firstFramePlans[0];
|
||||
EXPECT_TRUE(firstFramePlans[0].postProcess.IsRequested());
|
||||
EXPECT_TRUE(firstFramePlans[0].finalOutput.IsRequested());
|
||||
EXPECT_EQ(
|
||||
firstFrameRequests[0].GetMainSceneSurface().GetColorStateBefore(),
|
||||
firstFramePlan.GetMainSceneSurface().GetColorStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::Common);
|
||||
EXPECT_EQ(
|
||||
firstFrameRequests[0].postProcess.destinationSurface.GetColorStateBefore(),
|
||||
firstFramePlan.postProcess.destinationSurface.GetColorStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::Common);
|
||||
|
||||
RenderPassSequence postProcessPasses;
|
||||
postProcessPasses.AddPass(std::make_unique<MockScenePass>(pipelineState, "postProcess"));
|
||||
RenderPassSequence finalOutputPasses;
|
||||
finalOutputPasses.AddPass(std::make_unique<MockScenePass>(pipelineState, "finalOutput"));
|
||||
firstFrameRequests[0].postProcess.passes = &postProcessPasses;
|
||||
firstFrameRequests[0].finalOutput.passes = &finalOutputPasses;
|
||||
firstFramePlans[0].postProcess.passes = &postProcessPasses;
|
||||
firstFramePlans[0].finalOutput.passes = &finalOutputPasses;
|
||||
|
||||
ASSERT_TRUE(renderer.Render(firstFrameRequests));
|
||||
ASSERT_TRUE(renderer.Render(firstFramePlans));
|
||||
|
||||
const std::vector<CameraRenderRequest> secondFrameRequests =
|
||||
renderer.BuildRenderRequests(scene, nullptr, context, surface);
|
||||
ASSERT_EQ(secondFrameRequests.size(), 1u);
|
||||
const std::vector<CameraFramePlan> secondFramePlans =
|
||||
renderer.BuildFramePlans(scene, nullptr, context, surface);
|
||||
ASSERT_EQ(secondFramePlans.size(), 1u);
|
||||
const CameraFramePlan secondFramePlan = secondFramePlans[0];
|
||||
EXPECT_EQ(
|
||||
secondFrameRequests[0].GetMainSceneSurface().GetColorStateBefore(),
|
||||
secondFramePlan.GetMainSceneSurface().GetColorStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
EXPECT_EQ(
|
||||
secondFrameRequests[0].postProcess.destinationSurface.GetColorStateBefore(),
|
||||
secondFramePlan.postProcess.destinationSurface.GetColorStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
EXPECT_EQ(
|
||||
secondFrameRequests[0].finalOutput.sourceSurface.GetColorStateBefore(),
|
||||
secondFramePlan.finalOutput.sourceSurface.GetColorStateBefore(),
|
||||
XCEngine::RHI::ResourceStates::PixelShaderResource);
|
||||
|
||||
delete depthView;
|
||||
|
||||
@@ -175,11 +175,8 @@ TEST(SceneRenderRequestPlanner_Test, BuildsDirectionalShadowPlanForBaseCameraWhe
|
||||
EXPECT_EQ(request.directionalShadow.mapHeight, 2048u);
|
||||
EXPECT_EQ(request.directionalShadow.lightDirection, XCEngine::Math::Vector3::Back());
|
||||
EXPECT_GT(request.directionalShadow.focusPoint.z, 0.0f);
|
||||
EXPECT_TRUE(request.shadowCaster.hasCameraDataOverride);
|
||||
EXPECT_FALSE(request.shadowCaster.hasCameraDataOverride);
|
||||
EXPECT_EQ(request.shadowCaster.clearFlags, RenderClearFlags::Depth);
|
||||
EXPECT_EQ(request.shadowCaster.cameraDataOverride.viewportWidth, 2048u);
|
||||
EXPECT_EQ(request.shadowCaster.cameraDataOverride.viewportHeight, 2048u);
|
||||
EXPECT_EQ(request.shadowCaster.cameraDataOverride.clearFlags, RenderClearFlags::Depth);
|
||||
}
|
||||
|
||||
TEST(SceneRenderRequestPlanner_Test, SkipsDirectionalShadowPlanForOverlayCameraWhenBaseCameraExists) {
|
||||
@@ -211,7 +208,7 @@ TEST(SceneRenderRequestPlanner_Test, SkipsDirectionalShadowPlanForOverlayCameraW
|
||||
ASSERT_EQ(requests.size(), 2u);
|
||||
EXPECT_EQ(requests[0].camera, baseCamera);
|
||||
EXPECT_TRUE(requests[0].directionalShadow.enabled);
|
||||
EXPECT_TRUE(requests[0].shadowCaster.hasCameraDataOverride);
|
||||
EXPECT_FALSE(requests[0].shadowCaster.hasCameraDataOverride);
|
||||
|
||||
EXPECT_EQ(requests[1].camera, overlayCamera);
|
||||
EXPECT_FALSE(requests[1].directionalShadow.enabled);
|
||||
@@ -259,8 +256,7 @@ TEST(SceneRenderRequestPlanner_Test, AppliesConfiguredDirectionalShadowPlanningS
|
||||
ASSERT_TRUE(request.directionalShadow.IsValid());
|
||||
EXPECT_EQ(request.directionalShadow.mapWidth, 2048u);
|
||||
EXPECT_EQ(request.directionalShadow.mapHeight, 2048u);
|
||||
EXPECT_EQ(request.shadowCaster.cameraDataOverride.viewportWidth, 2048u);
|
||||
EXPECT_EQ(request.shadowCaster.cameraDataOverride.viewportHeight, 2048u);
|
||||
EXPECT_FALSE(request.shadowCaster.hasCameraDataOverride);
|
||||
EXPECT_GE(request.directionalShadow.focusPoint.z, 6.0f);
|
||||
EXPECT_LE(request.directionalShadow.focusPoint.z, 6.3f);
|
||||
EXPECT_FLOAT_EQ(request.directionalShadow.sampling.receiverDepthBias, 0.0025f);
|
||||
@@ -300,7 +296,7 @@ TEST(SceneRenderRequestPlanner_Test, SanitizesInvalidDirectionalShadowPlanningSe
|
||||
EXPECT_FLOAT_EQ(settings.perspectiveFocusFactor, 1.0f);
|
||||
EXPECT_FLOAT_EQ(settings.orthographicFocusFactor, 2.0f);
|
||||
EXPECT_FLOAT_EQ(settings.minDepthRange, 20.0f);
|
||||
EXPECT_FLOAT_EQ(settings.boundsPadding, 1.0f);
|
||||
EXPECT_FLOAT_EQ(settings.boundsPadding, 0.5f);
|
||||
EXPECT_FLOAT_EQ(settings.minDepthPadding, 2.0f);
|
||||
EXPECT_FLOAT_EQ(settings.sampling.receiverDepthBias, 0.0010f);
|
||||
EXPECT_FLOAT_EQ(settings.sampling.normalBiasScale, 2.0f);
|
||||
@@ -309,6 +305,53 @@ TEST(SceneRenderRequestPlanner_Test, SanitizesInvalidDirectionalShadowPlanningSe
|
||||
EXPECT_EQ(settings.casterBias.depthBiasUnits, 4);
|
||||
}
|
||||
|
||||
TEST(SceneRenderRequestPlanner_Test, UsesDirectionalLightShadowOverridesWhenEnabled) {
|
||||
Scene scene("SceneRenderRequestPlannerDirectionalShadowOverrides");
|
||||
|
||||
GameObject* cameraObject = scene.CreateGameObject("Camera");
|
||||
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
||||
camera->SetStackType(CameraStackType::Base);
|
||||
camera->SetDepth(1.0f);
|
||||
camera->SetProjectionType(CameraProjectionType::Perspective);
|
||||
camera->SetNearClipPlane(0.3f);
|
||||
camera->SetFarClipPlane(100.0f);
|
||||
|
||||
GameObject* shadowLightObject = scene.CreateGameObject("ShadowLight");
|
||||
auto* shadowLight = shadowLightObject->AddComponent<LightComponent>();
|
||||
shadowLight->SetLightType(LightType::Directional);
|
||||
shadowLight->SetCastsShadows(true);
|
||||
shadowLight->SetOverridesDirectionalShadowSettings(true);
|
||||
shadowLight->SetDirectionalShadowReceiverDepthBias(0.0035f);
|
||||
shadowLight->SetDirectionalShadowNormalBiasScale(1.25f);
|
||||
shadowLight->SetDirectionalShadowStrength(0.6f);
|
||||
shadowLight->SetDirectionalShadowDepthBiasFactor(3.25f);
|
||||
shadowLight->SetDirectionalShadowDepthBiasUnits(7);
|
||||
|
||||
SceneRenderRequestPlanner planner;
|
||||
DirectionalShadowPlanningSettings settings = {};
|
||||
settings.sampling.receiverDepthBias = 0.0010f;
|
||||
settings.sampling.normalBiasScale = 2.0f;
|
||||
settings.sampling.shadowStrength = 0.85f;
|
||||
settings.casterBias.depthBiasFactor = 2.5f;
|
||||
settings.casterBias.depthBiasUnits = 4;
|
||||
planner.SetDirectionalShadowPlanningSettings(settings);
|
||||
|
||||
const std::vector<CameraRenderRequest> requests = planner.BuildRequests(
|
||||
scene,
|
||||
nullptr,
|
||||
CreateValidContext(),
|
||||
RenderSurface(640, 360));
|
||||
|
||||
ASSERT_EQ(requests.size(), 1u);
|
||||
const CameraRenderRequest& request = requests[0];
|
||||
ASSERT_TRUE(request.directionalShadow.IsValid());
|
||||
EXPECT_FLOAT_EQ(request.directionalShadow.sampling.receiverDepthBias, 0.0035f);
|
||||
EXPECT_FLOAT_EQ(request.directionalShadow.sampling.normalBiasScale, 1.25f);
|
||||
EXPECT_FLOAT_EQ(request.directionalShadow.sampling.shadowStrength, 0.6f);
|
||||
EXPECT_FLOAT_EQ(request.directionalShadow.casterBias.depthBiasFactor, 3.25f);
|
||||
EXPECT_EQ(request.directionalShadow.casterBias.depthBiasUnits, 7);
|
||||
}
|
||||
|
||||
TEST(SceneRenderRequestPlanner_Test, IgnoresReceiveOnlyMeshesWhenFittingDirectionalShadowBounds) {
|
||||
Scene scene("SceneRenderRequestPlannerReceiveOnlyShadowBounds");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user