From 712f99e7233624c873ab0e32b7c44faba9d32503 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Mon, 13 Apr 2026 22:16:04 +0800 Subject: [PATCH] Refactor rendering frame execution contracts --- ...r_C++层第一阶段收口计划_2026-04-13_晚间.md | 310 ++++++++++++++++++ .../Rendering/Execution/CameraFramePlan.h | 179 ++++++++++ .../Rendering/Execution/CameraRenderer.h | 10 +- .../Rendering/Execution/DrawSettings.h | 15 + .../Execution/FrameExecutionContext.h | 56 ++++ .../XCEngine/Rendering/Execution/ScenePhase.h | 40 +++ .../Rendering/Execution/SceneRenderer.h | 14 +- .../Rendering/FrameData/CullingResults.h | 85 +++++ .../Rendering/FrameData/RenderSceneData.h | 18 + .../Rendering/FrameData/RendererListUtils.h | 145 ++++++++ .../Passes/BuiltinDepthStylePassBase.h | 2 + .../Passes/BuiltinGaussianSplatPass.h | 8 +- .../Passes/BuiltinShadowCasterPass.h | 1 + .../Rendering/Passes/BuiltinVolumetricPass.h | 8 +- .../Pipelines/BuiltinForwardPipeline.h | 35 +- .../Planning/SceneRenderRequestPlanner.h | 2 +- .../include/XCEngine/Rendering/RenderPass.h | 18 + .../XCEngine/Rendering/RenderPipeline.h | 7 + .../Rendering/SceneRenderFeaturePass.h | 24 ++ .../Rendering/Execution/CameraRenderer.cpp | 152 +++++---- .../src/Rendering/Execution/SceneRenderer.cpp | 145 +++++--- .../Extraction/RenderSceneExtractor.cpp | 18 + .../Passes/BuiltinDepthStylePassBase.cpp | 20 +- .../Passes/BuiltinGaussianSplatPass.cpp | 10 + .../Rendering/Passes/BuiltinObjectIdPass.cpp | 10 +- .../Passes/BuiltinShadowCasterPass.cpp | 4 + .../Passes/BuiltinVolumetricPass.cpp | 10 + .../Pipelines/BuiltinForwardPipeline.cpp | 255 +++++++++----- .../BuiltinForwardPipelineResources.cpp | 34 +- .../Planning/SceneRenderRequestPlanner.cpp | 10 +- 30 files changed, 1398 insertions(+), 247 deletions(-) create mode 100644 docs/plan/Renderer_C++层第一阶段收口计划_2026-04-13_晚间.md create mode 100644 engine/include/XCEngine/Rendering/Execution/CameraFramePlan.h create mode 100644 engine/include/XCEngine/Rendering/Execution/DrawSettings.h create mode 100644 engine/include/XCEngine/Rendering/Execution/FrameExecutionContext.h create mode 100644 engine/include/XCEngine/Rendering/Execution/ScenePhase.h create mode 100644 engine/include/XCEngine/Rendering/FrameData/CullingResults.h create mode 100644 engine/include/XCEngine/Rendering/FrameData/RendererListUtils.h create mode 100644 engine/include/XCEngine/Rendering/SceneRenderFeaturePass.h diff --git a/docs/plan/Renderer_C++层第一阶段收口计划_2026-04-13_晚间.md b/docs/plan/Renderer_C++层第一阶段收口计划_2026-04-13_晚间.md new file mode 100644 index 00000000..9dd2dc8c --- /dev/null +++ b/docs/plan/Renderer_C++层第一阶段收口计划_2026-04-13_晚间.md @@ -0,0 +1,310 @@ +# Renderer C++层第一阶段收口计划 + +日期: `2026-04-13` + +## 1. 文档定位 + +这份文档是 `Render Graph` 之前的最后一轮 `native render kernel v1` 收口计划。 + +目标不是继续往当前 `Rendering` 层里塞新功能,而是先把下面几件事彻底做清楚: + +- `request -> frame plan -> execution` 三层语义分开。 +- `RenderSceneExtractor -> CullingResults -> RendererList -> DrawSettings` 这条数据组织链正式化。 +- `BuiltinForwardPipeline` 从“大杂烩渲染器”收成“协调器 + 子能力”。 +- `Editor` 注入点、阴影、主场景、后处理之间的边界说清楚。 +- 给下一步 `C++ Render Graph` 留出稳定输入,而不是把当前混乱直接 graph 化。 + +结论先写在前面: + +- `Render Graph` 应该做在 `C++ native` 层。 +- 现在还不应该直接开做 `Render Graph` 或 `Deferred Renderer`。 +- 当前更优先的是把现有渲染层整理干净,再往 SRP/URP 方向演进。 + +## 2. 当前阶段已经完成的收口 + +截至 `2026-04-13` 晚间,已经落地的内容: + +- `CameraRenderRequest -> CameraFramePlan` 已经分层,`CameraRenderer` 开始正式消费 plan。 +- `CullingResults / RendererList` contract 已经进入 `RenderSceneData`。 +- `Forward / Depth / ObjectId` 已经优先走 `RendererList`,旧 `visibleItems` 只保留过渡 fallback。 +- `RendererListUtils` 已经把可见项遍历规则收口成公共工具。 +- `Gaussian / Volumetric` 已经从 `BuiltinForwardPipeline` 中抽成统一 `SceneRenderFeaturePass`。 +- `FrameExecutionContext / ScenePhase / DrawSettings` 已经引入。 +- `RenderPipeline` 已经支持新的 `FrameExecutionContext` 入口,旧三参数入口保留为兼容适配层。 +- `BuiltinForwardPipeline` 已经开始按 scene phase 组织主场景执行。 +- `CameraRenderer` 主场景阶段已经走新的 execution context,而不是继续只传三参数。 + +本轮验证结果: + +- `XCEditor` 已重新编译通过。 +- `rendering_unit_tests` 已重新编译通过。 +- `editor_tests` 已重新编译通过。 +- `editor_tests --gtest_filter=*ViewportRenderFlow*:*SceneViewportRenderPassBundle* --gtest_brief=1` 通过 `14/14`。 +- `rendering_unit_tests --gtest_filter=*BuiltinForwardPipeline*:*RenderPass*:*CameraSceneRenderer*:*Shadow*:*ObjectId* --gtest_brief=1` 通过 `67/68`。 +- 唯一失败仍然是既有老问题: + `BuiltinForwardPipeline_Test.OpenGLRuntimeTranspilesForwardShadowVariantToLegacyClipConventions` + +## 3. 为什么现在还不能直接上 Render Graph + +现在直接上 `Render Graph`,本质上是在 graph 里继续承接当前的隐式耦合,问题只会换个位置存在。 + +当前还没完全收口的核心问题不是“没有 graph”,而是下面这些边界还不够正式: + +- `CameraRenderer` 和各类 scene pass 之间,谁负责组织阶段,谁负责执行,还没有完全模块化。 +- `Shadow` 相关逻辑仍然分散在 planner、camera execution、pipeline、surface cache 几处。 +- `BuiltinForwardPipeline` 虽然已经开始瘦身,但还没有彻底收成一个明确的 coordinator。 +- `Editor` 的扩展点虽然已可用,但 contract 还偏“约定式”,还不够像以后给 `C# SRP` 暴露的正式接口。 +- 目录结构仍然在暴露旧历史:一些“子系统”目前还是文件堆,不是真正的模块。 + +如果这些问题不先解决,`Render Graph` 只会变成一个新的承压层,后面再拆更贵。 + +## 4. Render Graph 和未来 SRP/URP 的边界 + +未来的推荐结构仍然是这条线: + +`RHI -> Native Render Kernel -> Render Graph -> SRP-like Contract -> Universal Renderer -> C# Custom Pipeline` + +其中边界应当明确成这样: + +- `C++ native` 保留: + - `Render Graph` + - `CullingResults / RendererList` + - draw / dispatch context + - GPU resource cache + - shadow map / atlas 执行能力 + - Gaussian 排序与 working set + - Volume 资源上传与底层执行 +- `URP-like` 包层保留: + - feature 编排 + - pass 注入顺序 + - renderer feature 配置 + - 用户级 pipeline 组合 + - 未来的 `C#` 自定义渲染管线 API + +这意味着: + +- 阴影、Gaussian、Volumetric 不是整块搬去包层。 +- 包层更像 Unity 的 `URP renderer` 和 `renderer feature`。 +- native 层更像 Unity 没公开给用户、但真正负责执行和图资源生命周期的内核。 + +## 5. 第一阶段还差哪些工作才能收口 + +### 5.1 Shadow 子系统正式化 + +这是当前最值得先做的一块。 + +现状问题: + +- 阴影规划在 `Planning`。 +- 阴影 surface/cache 在 `Execution/Caches`。 +- 阴影执行又是 standalone pass。 +- 主场景采样阴影的状态切换仍在 `BuiltinForwardPipeline`。 + +这说明阴影现在“能跑”,但还不是一个正式子系统。 + +收口目标: + +- 明确 `shadow planning / shadow resources / shadow execution / shadow sampling contract` 四层边界。 +- 把方向光阴影相关共享数据收成一组正式类型,而不是散落在多个调用点上。 +- 为以后扩展 `cascades / shadow atlas / additional light shadows / baked shadowmask` 留出位置。 + +### 5.2 Editor 注入点正式化 + +现状问题: + +- 当前有 `pre scene / post scene / overlay / object id / outline / grid` 多种 editor 路径。 +- 这些能力已经能工作,但还没有统一成“正式 stage 注入 contract”。 + +收口目标: + +- 明确哪些注入点属于 runtime 正式阶段,哪些属于 editor-only extension。 +- 让 `CameraFramePlan` 对这些阶段的描述更加稳定。 +- 给以后 `C# renderer feature` 的注入点设计提供 native 对照物。 + +### 5.3 BuiltinForwardPipeline 继续瘦身 + +本轮已经完成第一刀,但还没收完。 + +还需要继续做的事情: + +- 把 forward scene phase 执行和底层 draw/resource 逻辑彻底分开。 +- 把 forward renderer 自己的内部资源、skybox、feature 协调代码进一步收拢。 +- 保证 `BuiltinForwardPipeline` 自己只表达“阶段顺序和协调”,不再继续膨胀。 + +### 5.4 旧 fallback 的裁边 + +当前有一些过渡兼容层是故意保留的: + +- `RenderPipeline::Render(context, surface, sceneData)` 旧入口 +- `visibleItems` fallback +- `BuildRenderRequests` 旧习惯入口 + +这些现在不该硬删,因为会影响现有 tests 和 editor 链路。 + +但第一阶段收口前,至少要做到: + +- 新主链默认只走正式 contract。 +- 旧入口只作为 adapter,不再新增依赖。 +- 每个 fallback 都有明确退出条件。 + +### 5.5 测试和文档补齐 + +当前已有回归还不够系统。 + +还需要补的重点测试: + +- `FrameExecutionContext` 是否完整透传 source surface / source color view / state。 +- `BuiltinForwardPipeline` scene phase 顺序是否稳定。 +- feature pass 的 `Prepare -> Execute` 顺序是否稳定。 +- editor 注入点在有后处理和无后处理两种情况下是否仍能保持正确 source/destination surface。 +- shadow planning 和 main scene shadow sampling 之间的契约测试。 + +## 6. 下一步最优先该做什么 + +下一步最优先建议做: + +`Shadow 子系统收口` + +原因很直接: + +- 它横跨 `Planning / Execution / Pipeline / Resources / Sampling`。 +- 它是以后 `Deferred / Light Baking / ShadowMask / Additional Light Shadows` 的共同基础。 +- 它也是未来 `Render Graph` 最敏感的一类资源依赖链。 +- 如果阴影先不收,后面 graph 化时 barrier、resource lifetime、pass dependency 都会更乱。 + +建议顺序: + +1. 先把方向光阴影的 plan/data/resource/execute contract 固化。 +2. 再把 editor 注入 contract 固化。 +3. 然后做第一轮目录收拢。 +4. 最后判断这一阶段是否可以正式收口,进入 `Render Graph` 设计。 + +## 7. 当前 Rendering 目录结构存在的问题 + +### 7.1 `FrameData` 目录太杂 + +现在 `FrameData` 里同时混着: + +- camera/environment 数据 +- scene snapshot +- visible item +- culling results +- renderer list utils + +这说明它不是一个明确模块,而是“跟帧有关的先放这”。 + +建议方向: + +- `FrameData` 保留纯只读帧快照类型。 +- 把 `CullingResults / RendererList / Filtering / Sorting / DrawSettings` 逐步收进更明确的 `Execution` 或 `Culling` 子域。 +- `RendererListUtils` 这种执行辅助逻辑,不适合长期留在 `FrameData`。 + +### 7.2 `Passes` 目录混合了三类东西 + +现在 `Passes` 里同时有: + +- 主场景 pass +- fullscreen/post-process pass +- editor pass + +这会让“runtime 正式渲染能力”和“editor 辅助渲染能力”继续混在一起。 + +建议方向: + +- `Passes/Scene` +- `Passes/Fullscreen` +- `Passes/Editor` +- `Passes/Shadow` + +不一定要一次性全搬,但后面新增文件不应继续平铺。 + +### 7.3 `BuiltinForwardPipeline` 还没形成真正子模块 + +当前 forward 相关代码已经分散在: + +- `BuiltinForwardPipeline.h/.cpp` +- `BuiltinForwardPipelineSkybox.cpp` +- `Internal/BuiltinForwardPipelineResources.cpp` + +这已经具备“应该单独成目录”的条件。 + +建议方向: + +- `Rendering/Pipelines/BuiltinForward/` + - `BuiltinForwardPipeline.h` + - `BuiltinForwardPipeline.cpp` + - `BuiltinForwardPipelineScene.cpp` + - `BuiltinForwardPipelineResources.cpp` + - `BuiltinForwardPipelineSkybox.cpp` + +这样后面 forward / deferred / universal renderer 才能并列演进。 + +### 7.4 `Shadow` 目录还不算真正的 shadow 子系统 + +目前虽然已经有 `Rendering/Shadow`,但从全链路看,阴影的责任仍然散在其他目录。 + +建议方向: + +- `Shadow` 目录最终至少容纳: + - planning types + - shared frame data + - runtime resources / caches + - shadow executor or shadow renderer + +### 7.5 `Internal` 边界不稳定 + +现在 `src/Rendering/Internal` 更像历史遗留缓冲区。 + +长期风险: + +- 真正属于 forward 的内部实现放在全局 `Internal` +- 真正属于 volume / splat / shadow 的内部实现也可能继续堆进去 + +建议方向: + +- 全局 `Internal` 只保留跨模块公共 helper。 +- 各子系统自己的内部文件放回各自目录。 + +## 8. 建议的第一轮目录收拢动作 + +这一轮只做低风险收拢,不做大搬家。 + +建议动作: + +- 把 `BuiltinForwardPipeline*` 相关实现收成 `Pipelines/BuiltinForward/` 子目录。 +- 给 `Passes` 至少先分出 `Editor` 和 `Fullscreen` 两个子目录。 +- 给 `Shadow` 补一组正式 shared types,再决定是否移动更多实现文件。 +- 暂时不大动 `include` 层 public 路径,先保证编译和引用稳定。 + +原则: + +- 先按职责拆,再按目录搬。 +- 不为了“好看”做纯目录手术。 +- 每次收拢都必须带编译和回归。 + +## 9. 第一阶段收口完成的判定标准 + +满足下面这些条件,就可以认为 `Render Graph` 之前这一阶段基本收口: + +- `CameraFramePlan` 成为主执行入口,旧 request 入口只保留兼容壳。 +- `FrameExecutionContext / ScenePhase / DrawSettings` 成为主场景执行正式 contract。 +- `CullingResults / RendererList` 成为主要 draw organization contract。 +- `BuiltinForwardPipeline` 被收成 coordinator,而不是继续长成总垃圾堆。 +- `Shadow` 成为清晰子系统,而不是散点逻辑。 +- `Editor` 注入点形成稳定 contract。 +- 目录结构不再继续鼓励“新逻辑直接往老文件里塞”。 +- 相关 tests 和编译链稳定。 + +到这一步,再开始 `C++ Render Graph`,代价才是可控的。 + +## 10. 当前建议 + +当前最佳路线不变: + +- 短期:继续按 Unity 的 `SRP + URP` 方向做 native 基座。 +- 中期:先在 `C++` 做出稳定的 `Render Graph + RendererList + Feature Injection` 内核。 +- 中长期:再暴露 `C#` 自定义渲染管线接口,做你自己的 `URP-like` 包层。 + +眼下最正确的动作不是“赶紧补 deferred”,也不是“先把所有特效搬去包层”,而是: + +`先把当前 native rendering layer 整理成一个真正可扩展的内核。` diff --git a/engine/include/XCEngine/Rendering/Execution/CameraFramePlan.h b/engine/include/XCEngine/Rendering/Execution/CameraFramePlan.h new file mode 100644 index 00000000..1d346816 --- /dev/null +++ b/engine/include/XCEngine/Rendering/Execution/CameraFramePlan.h @@ -0,0 +1,179 @@ +#pragma once + +#include + +namespace XCEngine { +namespace Rendering { + +struct CameraFramePlan { + CameraRenderRequest request = {}; + ShadowCasterRenderRequest shadowCaster = {}; + DirectionalShadowRenderPlan directionalShadow = {}; + PostProcessRenderRequest postProcess = {}; + FinalOutputRenderRequest finalOutput = {}; + ResolvedFinalColorPolicy finalColorPolicy = {}; + RenderPassSequence* preScenePasses = nullptr; + RenderPassSequence* postScenePasses = nullptr; + RenderPassSequence* overlayPasses = nullptr; + + static CameraFramePlan FromRequest(const CameraRenderRequest& request) { + CameraFramePlan plan = {}; + plan.request = request; + plan.shadowCaster = request.shadowCaster; + plan.directionalShadow = request.directionalShadow; + plan.postProcess = request.postProcess; + plan.finalOutput = request.finalOutput; + plan.finalColorPolicy = request.finalColorPolicy; + plan.preScenePasses = request.preScenePasses; + plan.postScenePasses = request.postScenePasses; + plan.overlayPasses = request.overlayPasses; + return plan; + } + + bool IsValid() const { + return request.IsValid(); + } + + 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 request.depthOnly.IsRequested(); + case CameraFrameStage::MainScene: + return true; + case CameraFrameStage::PostProcess: + return postProcess.IsRequested(); + case CameraFrameStage::FinalOutput: + return finalOutput.IsRequested(); + case CameraFrameStage::ObjectId: + return request.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 RenderSurface& GetMainSceneSurface() const { + if (postProcess.IsRequested()) { + return postProcess.sourceSurface; + } + + if (finalOutput.IsRequested()) { + return finalOutput.sourceSurface; + } + + return request.surface; + } + + const RenderSurface& GetFinalCompositedSurface() const { + if (finalOutput.IsRequested()) { + return finalOutput.destinationSurface; + } + + if (postProcess.IsRequested()) { + return postProcess.destinationSurface; + } + + return request.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 request.depthOnly.IsRequested() ? &request.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 request.objectId.IsRequested() ? &request.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(); + } +}; + +inline CameraRenderRequest BuildLegacyCameraRenderRequest( + const CameraFramePlan& plan) { + CameraRenderRequest request = plan.request; + request.shadowCaster = plan.shadowCaster; + request.directionalShadow = plan.directionalShadow; + request.postProcess = plan.postProcess; + request.finalOutput = plan.finalOutput; + request.finalColorPolicy = plan.finalColorPolicy; + request.preScenePasses = plan.preScenePasses; + request.postScenePasses = plan.postScenePasses; + request.overlayPasses = plan.overlayPasses; + return request; +} + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/Execution/CameraRenderer.h b/engine/include/XCEngine/Rendering/Execution/CameraRenderer.h index 32d4d820..4e2f4f82 100644 --- a/engine/include/XCEngine/Rendering/Execution/CameraRenderer.h +++ b/engine/include/XCEngine/Rendering/Execution/CameraRenderer.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -49,19 +50,20 @@ public: RenderPass* GetShadowCasterPass() const { return m_shadowCasterPass.get(); } bool Render(const CameraRenderRequest& request); + bool Render(const CameraFramePlan& plan); private: void ResetPipeline(std::unique_ptr pipeline); bool ResolveShadowCasterRequest( - const CameraRenderRequest& request, + const CameraFramePlan& plan, ShadowCasterRenderRequest& outResolvedShadowCaster, RHI::RHIResourceView*& outShadowMapView); - bool BuildSceneDataForRequest( - const CameraRenderRequest& request, + bool BuildSceneDataForPlan( + const CameraFramePlan& plan, RHI::RHIResourceView* shadowMapView, RenderSceneData& outSceneData); bool ExecuteRenderPlan( - const CameraRenderRequest& request, + const CameraFramePlan& plan, const ShadowCasterRenderRequest& resolvedShadowCaster, const RenderSceneData& sceneData); diff --git a/engine/include/XCEngine/Rendering/Execution/DrawSettings.h b/engine/include/XCEngine/Rendering/Execution/DrawSettings.h new file mode 100644 index 00000000..de169b2c --- /dev/null +++ b/engine/include/XCEngine/Rendering/Execution/DrawSettings.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace XCEngine { +namespace Rendering { + +struct DrawSettings { + ScenePhase scenePhase = ScenePhase::Opaque; + RendererListType rendererListType = RendererListType::AllVisible; +}; + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/Execution/FrameExecutionContext.h b/engine/include/XCEngine/Rendering/Execution/FrameExecutionContext.h new file mode 100644 index 00000000..c5f3ee20 --- /dev/null +++ b/engine/include/XCEngine/Rendering/Execution/FrameExecutionContext.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +namespace XCEngine { +namespace RHI { +class RHIResourceView; +} // namespace RHI + +namespace Rendering { + +struct RenderSceneData; +class RenderSurface; + +struct FrameExecutionContext { + FrameExecutionContext( + const RenderContext& inRenderContext, + const RenderSurface& inSurface, + const RenderSceneData& inSceneData, + const RenderSurface* inSourceSurface = nullptr, + RHI::RHIResourceView* inSourceColorView = nullptr, + RHI::ResourceStates inSourceColorState = RHI::ResourceStates::Common) + : renderContext(inRenderContext) + , surface(inSurface) + , sceneData(inSceneData) + , sourceSurface(inSourceSurface) + , sourceColorView(inSourceColorView) + , sourceColorState(inSourceColorState) { + } + + const RenderContext& renderContext; + const RenderSurface& surface; + const RenderSceneData& sceneData; + const RenderSurface* sourceSurface = nullptr; + RHI::RHIResourceView* sourceColorView = nullptr; + RHI::ResourceStates sourceColorState = RHI::ResourceStates::Common; +}; + +struct ScenePhaseExecutionContext { + ScenePhaseExecutionContext( + const FrameExecutionContext& inFrameContext, + ScenePhase inScenePhase, + bool inSampledDirectionalShadow = false) + : frameContext(inFrameContext) + , scenePhase(inScenePhase) + , sampledDirectionalShadow(inSampledDirectionalShadow) { + } + + FrameExecutionContext frameContext; + ScenePhase scenePhase = ScenePhase::Opaque; + bool sampledDirectionalShadow = false; +}; + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/Execution/ScenePhase.h b/engine/include/XCEngine/Rendering/Execution/ScenePhase.h new file mode 100644 index 00000000..08c63745 --- /dev/null +++ b/engine/include/XCEngine/Rendering/Execution/ScenePhase.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace XCEngine { +namespace Rendering { + +enum class ScenePhase : Core::uint32 { + Opaque = 0, + Skybox = 1, + Feature = 2, + Transparent = 3, + EditorExtension = 4, + PostProcess = 5, + FinalOutput = 6 +}; + +inline const char* ToString(ScenePhase scenePhase) { + switch (scenePhase) { + case ScenePhase::Opaque: + return "Opaque"; + case ScenePhase::Skybox: + return "Skybox"; + case ScenePhase::Feature: + return "Feature"; + case ScenePhase::Transparent: + return "Transparent"; + case ScenePhase::EditorExtension: + return "EditorExtension"; + case ScenePhase::PostProcess: + return "PostProcess"; + case ScenePhase::FinalOutput: + return "FinalOutput"; + } + + return "Unknown"; +} + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/Execution/SceneRenderer.h b/engine/include/XCEngine/Rendering/Execution/SceneRenderer.h index 099da603..1d4acb85 100644 --- a/engine/include/XCEngine/Rendering/Execution/SceneRenderer.h +++ b/engine/include/XCEngine/Rendering/Execution/SceneRenderer.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -35,9 +36,16 @@ public: Components::CameraComponent* overrideCamera, const RenderContext& context, const RenderSurface& surface); + std::vector BuildFramePlans( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface); bool Render(const CameraRenderRequest& request); bool Render(const std::vector& requests); + bool Render(const CameraFramePlan& plan); + bool Render(const std::vector& plans); bool Render( const Components::Scene& scene, Components::CameraComponent* overrideCamera, @@ -45,12 +53,14 @@ public: const RenderSurface& surface); private: + std::vector CreateFramePlansFromRequests( + const std::vector& requests) const; void PrepareOwnedFullscreenStageState(size_t requestCount); void ResolveCameraFinalColorPolicies( - std::vector& requests) const; + std::vector& plans) const; void AttachFullscreenStageRequests( const RenderContext& context, - std::vector& requests); + std::vector& plans); SceneRenderRequestPlanner m_requestPlanner; CameraRenderer m_cameraRenderer; diff --git a/engine/include/XCEngine/Rendering/FrameData/CullingResults.h b/engine/include/XCEngine/Rendering/FrameData/CullingResults.h new file mode 100644 index 00000000..5b44c910 --- /dev/null +++ b/engine/include/XCEngine/Rendering/FrameData/CullingResults.h @@ -0,0 +1,85 @@ +#pragma once + +#include + +#include +#include +#include + +namespace XCEngine { +namespace Rendering { + +enum class RendererListType : Core::uint32 { + AllVisible = 0, + Opaque = 1, + Transparent = 2, + ShadowCaster = 3, + ObjectId = 4 +}; + +enum class RendererSortMode : Core::uint32 { + None = 0, + FrontToBack = 1, + BackToFront = 2 +}; + +struct FilteringSettings { + Core::int32 renderQueueMin = std::numeric_limits::lowest(); + Core::int32 renderQueueMax = std::numeric_limits::max(); + bool requireShadowCasting = false; + bool requireRenderObjectId = false; +}; + +struct SortingSettings { + RendererSortMode sortMode = RendererSortMode::None; +}; + +struct RendererListDesc { + RendererListType type = RendererListType::AllVisible; + FilteringSettings filtering = {}; + SortingSettings sorting = {}; +}; + +struct RendererList { + RendererListDesc desc = {}; + std::vector visibleRenderItemIndices; + + bool Empty() const { + return visibleRenderItemIndices.empty(); + } + + size_t Size() const { + return visibleRenderItemIndices.size(); + } +}; + +struct CullingResults { + std::vector rendererLists; + + void Clear() { + rendererLists.clear(); + } + + RendererList* FindRendererList(RendererListType type) { + for (RendererList& rendererList : rendererLists) { + if (rendererList.desc.type == type) { + return &rendererList; + } + } + + return nullptr; + } + + const RendererList* FindRendererList(RendererListType type) const { + for (const RendererList& rendererList : rendererLists) { + if (rendererList.desc.type == type) { + return &rendererList; + } + } + + return nullptr; + } +}; + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/FrameData/RenderSceneData.h b/engine/include/XCEngine/Rendering/FrameData/RenderSceneData.h index 3b9bc731..501b0169 100644 --- a/engine/include/XCEngine/Rendering/FrameData/RenderSceneData.h +++ b/engine/include/XCEngine/Rendering/FrameData/RenderSceneData.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -115,6 +116,7 @@ struct RenderSceneData { RenderEnvironmentData environment; RenderLightingData lighting; Resources::ShaderKeywordSet globalShaderKeywords; + CullingResults cullingResults; std::vector visibleItems; std::vector visibleGaussianSplats; std::vector visibleVolumes; @@ -122,6 +124,22 @@ struct RenderSceneData { bool HasCamera() const { return camera != nullptr; } + + RendererList* FindRendererList(RendererListType type) { + return cullingResults.FindRendererList(type); + } + + const RendererList* FindRendererList(RendererListType type) const { + return cullingResults.FindRendererList(type); + } + + const VisibleRenderItem* TryGetVisibleRenderItem(Core::uint32 index) const { + if (index >= visibleItems.size()) { + return nullptr; + } + + return &visibleItems[index]; + } }; } // namespace Rendering diff --git a/engine/include/XCEngine/Rendering/FrameData/RendererListUtils.h b/engine/include/XCEngine/Rendering/FrameData/RendererListUtils.h new file mode 100644 index 00000000..ea7140e7 --- /dev/null +++ b/engine/include/XCEngine/Rendering/FrameData/RendererListUtils.h @@ -0,0 +1,145 @@ +#pragma once + +#include +#include +#include + +#include + +namespace XCEngine { +namespace Rendering { + +inline FilteringSettings BuildDefaultFilteringSettings(RendererListType type) { + FilteringSettings filtering = {}; + + switch (type) { + case RendererListType::AllVisible: + break; + case RendererListType::Opaque: + filtering.renderQueueMax = + static_cast(Resources::MaterialRenderQueue::Transparent) - 1; + break; + case RendererListType::Transparent: + filtering.renderQueueMin = + static_cast(Resources::MaterialRenderQueue::Transparent); + break; + case RendererListType::ShadowCaster: + filtering.requireShadowCasting = true; + break; + case RendererListType::ObjectId: + filtering.requireRenderObjectId = true; + break; + } + + return filtering; +} + +inline SortingSettings BuildDefaultSortingSettings(RendererListType type) { + SortingSettings sorting = {}; + + switch (type) { + case RendererListType::Opaque: + case RendererListType::ShadowCaster: + case RendererListType::ObjectId: + sorting.sortMode = RendererSortMode::FrontToBack; + break; + case RendererListType::Transparent: + sorting.sortMode = RendererSortMode::BackToFront; + break; + case RendererListType::AllVisible: + sorting.sortMode = RendererSortMode::None; + break; + } + + return sorting; +} + +inline RendererListDesc BuildDefaultRendererListDesc(RendererListType type) { + RendererListDesc desc = {}; + desc.type = type; + desc.filtering = BuildDefaultFilteringSettings(type); + desc.sorting = BuildDefaultSortingSettings(type); + return desc; +} + +inline bool MatchesFilteringSettings( + const VisibleRenderItem& visibleItem, + const FilteringSettings& filtering) { + if (visibleItem.renderQueue < filtering.renderQueueMin || + visibleItem.renderQueue > filtering.renderQueueMax) { + return false; + } + + if (filtering.requireShadowCasting && + visibleItem.meshRenderer != nullptr && + !visibleItem.meshRenderer->GetCastShadows()) { + return false; + } + + if (filtering.requireRenderObjectId && + !IsValidRenderObjectId(visibleItem.renderObjectId)) { + return false; + } + + return true; +} + +inline bool MatchesRendererListDesc( + const VisibleRenderItem& visibleItem, + const RendererListDesc& desc) { + return MatchesFilteringSettings(visibleItem, desc.filtering); +} + +inline RendererList BuildRendererList( + RendererListType type, + const std::vector& visibleItems) { + RendererList rendererList = {}; + rendererList.desc = BuildDefaultRendererListDesc(type); + rendererList.visibleRenderItemIndices.reserve(visibleItems.size()); + + for (Core::uint32 visibleItemIndex = 0; + visibleItemIndex < static_cast(visibleItems.size()); + ++visibleItemIndex) { + if (!MatchesRendererListDesc( + visibleItems[visibleItemIndex], + rendererList.desc)) { + continue; + } + + rendererList.visibleRenderItemIndices.push_back(visibleItemIndex); + } + + return rendererList; +} + +template +inline void VisitRendererListVisibleItems( + const RenderSceneData& sceneData, + RendererListType rendererListType, + Visitor&& visitor) { + const RendererList* rendererList = sceneData.FindRendererList(rendererListType); + if (rendererList != nullptr) { + for (Core::uint32 visibleItemIndex : rendererList->visibleRenderItemIndices) { + const VisibleRenderItem* visibleItem = + sceneData.TryGetVisibleRenderItem(visibleItemIndex); + if (visibleItem == nullptr) { + continue; + } + + visitor(*visibleItem); + } + return; + } + + const RendererListDesc fallbackDesc = BuildDefaultRendererListDesc(rendererListType); + for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) { + if (!MatchesRendererListDesc(visibleItem, fallbackDesc)) { + continue; + } + + visitor(visibleItem); + } +} + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h b/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h index a20c5e12..df2e999c 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,7 @@ protected: BuiltinMaterialPass passType, Containers::String builtinShaderPath); + virtual RendererListType GetRendererListType() const; virtual bool ShouldRenderVisibleItem(const VisibleRenderItem& visibleItem) const; private: diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinGaussianSplatPass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinGaussianSplatPass.h index 22decc77..606c879e 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinGaussianSplatPass.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinGaussianSplatPass.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -40,12 +40,16 @@ class BuiltinGaussianSplatPassResources; namespace Passes { -class BuiltinGaussianSplatPass final : public RenderPass { +class BuiltinGaussianSplatPass final : public SceneRenderFeaturePass { public: ~BuiltinGaussianSplatPass() override; const char* GetName() const override; bool Initialize(const RenderContext& context) override; + bool IsActive(const RenderSceneData& sceneData) const override; + bool Prepare( + const RenderContext& context, + const RenderSceneData& sceneData) override; bool PrepareGaussianSplatResources( const RenderContext& context, const RenderSceneData& sceneData); diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h index 3d014795..22f10879 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h @@ -16,6 +16,7 @@ public: const char* GetName() const override; protected: + RendererListType GetRendererListType() const override; bool ShouldRenderVisibleItem(const VisibleRenderItem& visibleItem) const override; }; diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinVolumetricPass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinVolumetricPass.h index cfbd6c6a..95c86466 100644 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinVolumetricPass.h +++ b/engine/include/XCEngine/Rendering/Passes/BuiltinVolumetricPass.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -33,7 +33,7 @@ struct RenderLightingData; namespace Passes { -class BuiltinVolumetricPass final : public RenderPass { +class BuiltinVolumetricPass final : public SceneRenderFeaturePass { public: ~BuiltinVolumetricPass() override; @@ -41,6 +41,10 @@ public: const char* GetName() const override; bool Initialize(const RenderContext& context) override; + bool IsActive(const RenderSceneData& sceneData) const override; + bool Prepare( + const RenderContext& context, + const RenderSceneData& sceneData) override; bool PrepareVolumeResources( const RenderContext& context, const RenderSceneData& sceneData); diff --git a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h index 5fb6d673..35c65b72 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h +++ b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h @@ -1,11 +1,14 @@ #pragma once +#include +#include #include #include #include #include #include #include +#include #include #include @@ -50,16 +53,21 @@ public: BuiltinForwardPipeline(); ~BuiltinForwardPipeline() override; + using RenderPipeline::Render; + static RHI::InputLayoutDesc BuildInputLayout(); bool Initialize(const RenderContext& context) override; void Shutdown() override; + bool Render(const FrameExecutionContext& executionContext) override; bool Render( const RenderContext& context, const RenderSurface& surface, const RenderSceneData& sceneData) override; private: + using ForwardSceneFeaturePassArray = std::array; + struct OwnedDescriptorSet { RHI::RHIDescriptorPool* pool = nullptr; RHI::RHIDescriptorSet* set = nullptr; @@ -263,6 +271,19 @@ private: }; bool EnsureInitialized(const RenderContext& context); + ForwardSceneFeaturePassArray CollectForwardSceneFeaturePasses() const; + bool InitializeForwardSceneFeaturePasses(const RenderContext& context); + void ShutdownForwardSceneFeaturePasses(); + bool PrepareForwardSceneFeaturePasses( + const FrameExecutionContext& executionContext) const; + bool ExecuteForwardSceneFeaturePasses( + const ScenePhaseExecutionContext& executionContext) const; + ScenePhaseExecutionContext BuildScenePhaseExecutionContext( + const FrameExecutionContext& executionContext, + ScenePhase scenePhase) const; + DrawSettings BuildDrawSettings(ScenePhase scenePhase) const; + bool ExecuteForwardScene(const FrameExecutionContext& executionContext); + bool ExecuteScenePhase(const ScenePhaseExecutionContext& executionContext); bool CreatePipelineResources(const RenderContext& context); void DestroyPipelineResources(); static bool TryResolveSurfacePassType( @@ -315,18 +336,14 @@ private: bool HasProceduralSkybox(const RenderSceneData& sceneData) const; bool BeginForwardScenePass(const RenderPassContext& context); void EndForwardScenePass(const RenderPassContext& context); - bool ExecuteForwardOpaquePass(const RenderPassContext& context); + bool ExecuteForwardOpaquePass(const ScenePhaseExecutionContext& context); bool ExecuteForwardSkyboxPass(const RenderPassContext& context); - bool ExecuteForwardTransparentPass(const RenderPassContext& context); + bool ExecuteForwardTransparentPass(const ScenePhaseExecutionContext& context); bool DrawVisibleItems( - const RenderContext& context, - const RenderSurface& surface, - const RenderSceneData& sceneData, - bool drawTransparentItems); + const FrameExecutionContext& executionContext, + const DrawSettings& drawSettings); bool DrawVisibleItem( - const RenderContext& context, - const RenderSurface& surface, - const RenderSceneData& sceneData, + const FrameExecutionContext& executionContext, const VisibleRenderItem& visibleItem); bool EnsureSkyboxResources(const RenderContext& context); bool CreateSkyboxResources(const RenderContext& context); diff --git a/engine/include/XCEngine/Rendering/Planning/SceneRenderRequestPlanner.h b/engine/include/XCEngine/Rendering/Planning/SceneRenderRequestPlanner.h index f8464a2f..45439455 100644 --- a/engine/include/XCEngine/Rendering/Planning/SceneRenderRequestPlanner.h +++ b/engine/include/XCEngine/Rendering/Planning/SceneRenderRequestPlanner.h @@ -24,7 +24,7 @@ struct DirectionalShadowPlanningSettings { float perspectiveFocusFactor = 1.0f; float orthographicFocusFactor = 2.0f; float minDepthRange = 20.0f; - float boundsPadding = 1.0f; + float boundsPadding = 0.5f; float minDepthPadding = 2.0f; DirectionalShadowSamplingSettings sampling = {}; DirectionalShadowCasterBiasSettings casterBias = {}; diff --git a/engine/include/XCEngine/Rendering/RenderPass.h b/engine/include/XCEngine/Rendering/RenderPass.h index 7eb1d533..0e0a0579 100644 --- a/engine/include/XCEngine/Rendering/RenderPass.h +++ b/engine/include/XCEngine/Rendering/RenderPass.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -25,6 +26,23 @@ struct RenderPassContext { RHI::ResourceStates sourceColorState = RHI::ResourceStates::Common; }; +inline RenderPassContext BuildRenderPassContext( + const FrameExecutionContext& executionContext) { + return { + executionContext.renderContext, + executionContext.surface, + executionContext.sceneData, + executionContext.sourceSurface, + executionContext.sourceColorView, + executionContext.sourceColorState + }; +} + +inline RenderPassContext BuildRenderPassContext( + const ScenePhaseExecutionContext& executionContext) { + return BuildRenderPassContext(executionContext.frameContext); +} + class RenderPass { public: virtual ~RenderPass() = default; diff --git a/engine/include/XCEngine/Rendering/RenderPipeline.h b/engine/include/XCEngine/Rendering/RenderPipeline.h index 7d058224..26a9e939 100644 --- a/engine/include/XCEngine/Rendering/RenderPipeline.h +++ b/engine/include/XCEngine/Rendering/RenderPipeline.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -14,6 +15,12 @@ public: virtual bool Initialize(const RenderContext& context) = 0; virtual void Shutdown() = 0; + virtual bool Render(const FrameExecutionContext& executionContext) { + return Render( + executionContext.renderContext, + executionContext.surface, + executionContext.sceneData); + } virtual bool Render( const RenderContext& context, const RenderSurface& surface, diff --git a/engine/include/XCEngine/Rendering/SceneRenderFeaturePass.h b/engine/include/XCEngine/Rendering/SceneRenderFeaturePass.h new file mode 100644 index 00000000..4fc5155f --- /dev/null +++ b/engine/include/XCEngine/Rendering/SceneRenderFeaturePass.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace XCEngine { +namespace Rendering { + +class SceneRenderFeaturePass : public RenderPass { +public: + ~SceneRenderFeaturePass() override = default; + + virtual bool IsActive(const RenderSceneData& sceneData) const = 0; + + virtual bool Prepare( + const RenderContext& renderContext, + const RenderSceneData& sceneData) { + (void)renderContext; + (void)sceneData; + return true; + } +}; + +} // namespace Rendering +} // namespace XCEngine diff --git a/engine/src/Rendering/Execution/CameraRenderer.cpp b/engine/src/Rendering/Execution/CameraRenderer.cpp index 9cc2e32e..698897d0 100644 --- a/engine/src/Rendering/Execution/CameraRenderer.cpp +++ b/engine/src/Rendering/Execution/CameraRenderer.cpp @@ -289,81 +289,88 @@ bool ExecuteFullscreenPassSequenceStage( RenderPassContext BuildFrameStagePassContext( CameraFrameStage stage, - const CameraRenderRequest& request, + const CameraFramePlan& plan, const RenderSceneData& sceneData) { - const RenderSurface* outputSurface = request.GetOutputSurface(stage); + const RenderSurface* outputSurface = plan.GetOutputSurface(stage); return { - request.context, - outputSurface != nullptr ? *outputSurface : request.surface, + plan.request.context, + outputSurface != nullptr ? *outputSurface : plan.request.surface, sceneData, - request.GetSourceSurface(stage), - request.GetSourceColorView(stage), - request.GetSourceColorState(stage) + plan.GetSourceSurface(stage), + plan.GetSourceColorView(stage), + plan.GetSourceColorState(stage) }; } bool ExecuteFrameStage( CameraFrameStage stage, - const CameraRenderRequest& request, + const CameraFramePlan& plan, const ShadowCasterRenderRequest& resolvedShadowCaster, const RenderSceneData& sceneData, CameraFrameExecutionState& executionState) { - const RenderPassContext passContext = BuildFrameStagePassContext(stage, request, sceneData); + const RenderPassContext passContext = BuildFrameStagePassContext(stage, plan, sceneData); switch (stage) { case CameraFrameStage::PreScenePasses: return ExecutePassSequenceStage( executionState.preScenePasses, - request.GetPassSequence(stage), - request.context, + plan.GetPassSequence(stage), + plan.request.context, passContext); case CameraFrameStage::ShadowCaster: return ExecuteScenePassRequest( executionState.shadowCasterPass, resolvedShadowCaster, - request.context, + plan.request.context, sceneData); case CameraFrameStage::DepthOnly: return ExecuteScenePassRequest( executionState.depthOnlyPass, - request.depthOnly, - request.context, + plan.request.depthOnly, + plan.request.context, sceneData); case CameraFrameStage::MainScene: return executionState.pipeline != nullptr && - executionState.pipeline->Render(request.context, passContext.surface, sceneData); + executionState.pipeline->Render( + FrameExecutionContext( + plan.request.context, + passContext.surface, + sceneData, + passContext.sourceSurface, + passContext.sourceColorView, + passContext.sourceColorState)); case CameraFrameStage::PostProcess: return ExecuteFullscreenPassSequenceStage( executionState.postProcessPasses, - request.GetPassSequence(stage), - request.context, + plan.GetPassSequence(stage), + plan.request.context, passContext, executionState.postProcessSurfaceCache); case CameraFrameStage::FinalOutput: return ExecuteFullscreenPassSequenceStage( executionState.finalOutputPasses, - request.GetPassSequence(stage), - request.context, + plan.GetPassSequence(stage), + plan.request.context, passContext, executionState.finalOutputSurfaceCache); case CameraFrameStage::ObjectId: - return !request.objectId.IsRequested() || + return !plan.request.objectId.IsRequested() || ExecuteStandalonePass( executionState.objectIdPass, - request.context, - request.objectId.surface, + plan.request.context, + plan.request.objectId.surface, sceneData); case CameraFrameStage::PostScenePasses: return ExecutePassSequenceStage( executionState.postScenePasses, - request.GetPassSequence(stage), - request.context, + plan.GetPassSequence(stage), + plan.request.context, passContext); case CameraFrameStage::OverlayPasses: return ExecutePassSequenceStage( executionState.overlayPasses, - request.GetPassSequence(stage), - request.context, + plan.GetPassSequence(stage), + plan.request.context, passContext); default: return false; @@ -396,27 +403,27 @@ RenderDirectionalShadowData BuildDirectionalShadowData( return shadowData; } -RenderEnvironmentData BuildEnvironmentData(const CameraRenderRequest& request) { +RenderEnvironmentData BuildEnvironmentData(const CameraFramePlan& plan) { RenderEnvironmentData environment = {}; - const RenderSurface& mainSceneSurface = request.GetMainSceneSurface(); - if (request.camera == nullptr || + const RenderSurface& mainSceneSurface = plan.GetMainSceneSurface(); + if (plan.request.camera == nullptr || mainSceneSurface.GetDepthAttachment() == nullptr || - !HasRenderClearFlag(request.clearFlags, RenderClearFlags::Color) || - !request.camera->IsSkyboxEnabled() || - request.camera->GetProjectionType() != Components::CameraProjectionType::Perspective) { + !HasRenderClearFlag(plan.request.clearFlags, RenderClearFlags::Color) || + !plan.request.camera->IsSkyboxEnabled() || + plan.request.camera->GetProjectionType() != Components::CameraProjectionType::Perspective) { return environment; } - if (const Resources::Material* skyboxMaterial = request.camera->GetSkyboxMaterial()) { + if (const Resources::Material* skyboxMaterial = plan.request.camera->GetSkyboxMaterial()) { environment.mode = RenderEnvironmentMode::MaterialSkybox; environment.materialSkybox.material = skyboxMaterial; return environment; } environment.mode = RenderEnvironmentMode::ProceduralSkybox; - environment.skybox.topColor = request.camera->GetSkyboxTopColor(); - environment.skybox.horizonColor = request.camera->GetSkyboxHorizonColor(); - environment.skybox.bottomColor = request.camera->GetSkyboxBottomColor(); + environment.skybox.topColor = plan.request.camera->GetSkyboxTopColor(); + environment.skybox.horizonColor = plan.request.camera->GetSkyboxHorizonColor(); + environment.skybox.bottomColor = plan.request.camera->GetSkyboxBottomColor(); return environment; } @@ -538,17 +545,17 @@ void CameraRenderer::ResetPipeline(std::unique_ptr pipeline) { } bool CameraRenderer::ResolveShadowCasterRequest( - const CameraRenderRequest& request, + const CameraFramePlan& plan, ShadowCasterRenderRequest& outResolvedShadowCaster, RHI::RHIResourceView*& outShadowMapView) { - outResolvedShadowCaster = request.shadowCaster; + outResolvedShadowCaster = plan.shadowCaster; outShadowMapView = nullptr; if (outResolvedShadowCaster.IsRequested()) { return outResolvedShadowCaster.IsValid(); } - if (!request.directionalShadow.IsValid()) { + if (!plan.directionalShadow.IsValid()) { return true; } @@ -556,7 +563,7 @@ bool CameraRenderer::ResolveShadowCasterRequest( m_directionalShadowSurface = std::make_unique(); } - if (!m_directionalShadowSurface->EnsureSurface(request.context, request.directionalShadow)) { + if (!m_directionalShadowSurface->EnsureSurface(plan.request.context, plan.directionalShadow)) { return false; } @@ -564,44 +571,44 @@ bool CameraRenderer::ResolveShadowCasterRequest( outResolvedShadowCaster.clearFlags = RenderClearFlags::Depth; if (!outResolvedShadowCaster.hasCameraDataOverride) { outResolvedShadowCaster.hasCameraDataOverride = true; - outResolvedShadowCaster.cameraDataOverride = request.directionalShadow.cameraData; + outResolvedShadowCaster.cameraDataOverride = plan.directionalShadow.cameraData; } outShadowMapView = m_directionalShadowSurface->GetDepthShaderView(); return true; } -bool CameraRenderer::BuildSceneDataForRequest( - const CameraRenderRequest& request, +bool CameraRenderer::BuildSceneDataForPlan( + const CameraFramePlan& plan, RHI::RHIResourceView* shadowMapView, RenderSceneData& outSceneData) { - const RenderSurface& mainSceneSurface = request.GetMainSceneSurface(); + const RenderSurface& mainSceneSurface = plan.GetMainSceneSurface(); outSceneData = m_sceneExtractor.ExtractForCamera( - *request.scene, - *request.camera, + *plan.request.scene, + *plan.request.camera, mainSceneSurface.GetRenderAreaWidth(), mainSceneSurface.GetRenderAreaHeight()); if (!outSceneData.HasCamera()) { return false; } - if (request.directionalShadow.IsValid()) { + if (plan.directionalShadow.IsValid()) { outSceneData.lighting.mainDirectionalShadow = - BuildDirectionalShadowData(request.directionalShadow, shadowMapView); + BuildDirectionalShadowData(plan.directionalShadow, shadowMapView); } outSceneData.globalShaderKeywords = BuildSceneGlobalShaderKeywords(outSceneData); - outSceneData.cameraData.clearFlags = request.clearFlags; - outSceneData.environment = BuildEnvironmentData(request); - if (request.hasClearColorOverride) { - outSceneData.cameraData.clearColor = request.clearColorOverride; + outSceneData.cameraData.clearFlags = plan.request.clearFlags; + outSceneData.environment = BuildEnvironmentData(plan); + if (plan.request.hasClearColorOverride) { + outSceneData.cameraData.clearColor = plan.request.clearColorOverride; } return true; } bool CameraRenderer::ExecuteRenderPlan( - const CameraRenderRequest& request, + const CameraFramePlan& plan, const ShadowCasterRenderRequest& resolvedShadowCaster, const RenderSceneData& sceneData) { CameraFrameExecutionState executionState = {}; @@ -613,14 +620,14 @@ bool CameraRenderer::ExecuteRenderPlan( executionState.finalOutputSurfaceCache = m_finalOutputSurfaceCache.get(); for (const CameraFrameStageInfo& stageInfo : kOrderedCameraFrameStages) { - if (!request.HasFrameStage(stageInfo.stage) && + if (!plan.HasFrameStage(stageInfo.stage) && stageInfo.stage != CameraFrameStage::MainScene) { continue; } if (!ExecuteFrameStage( stageInfo.stage, - request, + plan, resolvedShadowCaster, sceneData, executionState)) { @@ -633,14 +640,19 @@ bool CameraRenderer::ExecuteRenderPlan( bool CameraRenderer::Render( const CameraRenderRequest& request) { - if (!request.IsValid() || m_pipeline == nullptr) { + return Render(CameraFramePlan::FromRequest(request)); +} + +bool CameraRenderer::Render( + const CameraFramePlan& plan) { + if (!plan.IsValid() || m_pipeline == nullptr) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, - "CameraRenderer::Render failed: request invalid or pipeline missing"); + "CameraRenderer::Render failed: plan invalid or pipeline missing"); return false; } - const RenderSurface& mainSceneSurface = request.GetMainSceneSurface(); + const RenderSurface& mainSceneSurface = plan.GetMainSceneSurface(); if (mainSceneSurface.GetRenderAreaWidth() == 0 || mainSceneSurface.GetRenderAreaHeight() == 0) { Debug::Logger::Get().Error( @@ -648,29 +660,29 @@ bool CameraRenderer::Render( "CameraRenderer::Render failed: main scene surface render area is empty"); return false; } - if (request.depthOnly.IsRequested() && - !request.depthOnly.IsValid()) { + if (plan.request.depthOnly.IsRequested() && + !plan.request.depthOnly.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: depth-only request invalid"); return false; } - if (request.postProcess.IsRequested() && - !request.postProcess.IsValid()) { + if (plan.postProcess.IsRequested() && + !plan.postProcess.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: post-process request invalid"); return false; } - if (request.finalOutput.IsRequested() && - !request.finalOutput.IsValid()) { + if (plan.finalOutput.IsRequested() && + !plan.finalOutput.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: final-output request invalid"); return false; } - if (request.objectId.IsRequested() && - !request.objectId.IsValid()) { + if (plan.request.objectId.IsRequested() && + !plan.request.objectId.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: object-id request invalid"); @@ -679,7 +691,7 @@ bool CameraRenderer::Render( ShadowCasterRenderRequest resolvedShadowCaster = {}; RHI::RHIResourceView* shadowMapView = nullptr; - if (!ResolveShadowCasterRequest(request, resolvedShadowCaster, shadowMapView)) { + if (!ResolveShadowCasterRequest(plan, resolvedShadowCaster, shadowMapView)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: ResolveShadowCasterRequest returned false"); @@ -687,14 +699,14 @@ bool CameraRenderer::Render( } RenderSceneData sceneData = {}; - if (!BuildSceneDataForRequest(request, shadowMapView, sceneData)) { + if (!BuildSceneDataForPlan(plan, shadowMapView, sceneData)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, - "CameraRenderer::Render failed: BuildSceneDataForRequest returned false"); + "CameraRenderer::Render failed: BuildSceneDataForPlan returned false"); return false; } - if (!ExecuteRenderPlan(request, resolvedShadowCaster, sceneData)) { + if (!ExecuteRenderPlan(plan, resolvedShadowCaster, sceneData)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "CameraRenderer::Render failed: ExecuteRenderPlan returned false"); diff --git a/engine/src/Rendering/Execution/SceneRenderer.cpp b/engine/src/Rendering/Execution/SceneRenderer.cpp index 7b866b9e..d6d027d4 100644 --- a/engine/src/Rendering/Execution/SceneRenderer.cpp +++ b/engine/src/Rendering/Execution/SceneRenderer.cpp @@ -13,6 +13,14 @@ namespace Rendering { namespace { +bool CompareCameraFramePlans( + const CameraFramePlan& lhs, + const CameraFramePlan& rhs) { + return SceneRenderRequestUtils::CompareCameraRenderRequests( + lhs.request, + rhs.request); +} + RenderSurface ConfigureFullscreenStageSurface( const FullscreenPassSurfaceCache::SurfaceEntry& entry, const RenderSurface& templateSurface, @@ -94,44 +102,72 @@ std::vector SceneRenderer::BuildRenderRequests( Components::CameraComponent* overrideCamera, const RenderContext& context, const RenderSurface& surface) { - std::vector requests = - m_requestPlanner.BuildRequests(scene, overrideCamera, context, surface); - ResolveCameraFinalColorPolicies(requests); - AttachFullscreenStageRequests(context, requests); + const std::vector plans = + BuildFramePlans(scene, overrideCamera, context, surface); + std::vector requests = {}; + requests.reserve(plans.size()); + for (const CameraFramePlan& plan : plans) { + requests.push_back(BuildLegacyCameraRenderRequest(plan)); + } return requests; } +std::vector SceneRenderer::BuildFramePlans( + const Components::Scene& scene, + Components::CameraComponent* overrideCamera, + const RenderContext& context, + const RenderSurface& surface) { + const std::vector requests = + m_requestPlanner.BuildRequests(scene, overrideCamera, context, surface); + std::vector plans = CreateFramePlansFromRequests(requests); + ResolveCameraFinalColorPolicies(plans); + AttachFullscreenStageRequests(context, plans); + return plans; +} + bool SceneRenderer::Render(const CameraRenderRequest& request) { - return m_cameraRenderer.Render(request); + return Render(CameraFramePlan::FromRequest(request)); } bool SceneRenderer::Render(const std::vector& requests) { - if (requests.empty()) { + std::vector plans = CreateFramePlansFromRequests(requests); + return Render(plans); +} + +bool SceneRenderer::Render(const CameraFramePlan& plan) { + return m_cameraRenderer.Render(plan); +} + +bool SceneRenderer::Render(const std::vector& plans) { + if (plans.empty()) { return false; } - for (const CameraRenderRequest& request : requests) { - if (!request.IsValid()) { + for (const CameraFramePlan& plan : plans) { + if (!plan.IsValid()) { return false; } } - std::vector sortedRequests = requests; - SceneRenderRequestUtils::SortCameraRenderRequests(sortedRequests); + std::vector sortedPlans = plans; + std::stable_sort( + sortedPlans.begin(), + sortedPlans.end(), + CompareCameraFramePlans); bool rendered = false; - for (const CameraRenderRequest& request : sortedRequests) { - if (!m_cameraRenderer.Render(request)) { + for (const CameraFramePlan& plan : sortedPlans) { + if (!m_cameraRenderer.Render(plan)) { return false; } UpdateTrackedFullscreenSurfaceState( m_ownedFullscreenStageSurfaces, - &request.GetMainSceneSurface()); - if (request.postProcess.IsRequested()) { + &plan.GetMainSceneSurface()); + if (plan.postProcess.IsRequested()) { UpdateTrackedFullscreenSurfaceState( m_ownedFullscreenStageSurfaces, - &request.postProcess.destinationSurface); + &plan.postProcess.destinationSurface); } rendered = true; @@ -145,7 +181,18 @@ bool SceneRenderer::Render( Components::CameraComponent* overrideCamera, const RenderContext& context, const RenderSurface& surface) { - return Render(BuildRenderRequests(scene, overrideCamera, context, surface)); + return Render(BuildFramePlans(scene, overrideCamera, context, surface)); +} + +std::vector SceneRenderer::CreateFramePlansFromRequests( + const std::vector& requests) const { + std::vector plans = {}; + plans.reserve(requests.size()); + for (const CameraRenderRequest& request : requests) { + plans.push_back(CameraFramePlan::FromRequest(request)); + } + + return plans; } void SceneRenderer::PrepareOwnedFullscreenStageState(size_t requestCount) { @@ -166,39 +213,39 @@ void SceneRenderer::PrepareOwnedFullscreenStageState(size_t requestCount) { } void SceneRenderer::ResolveCameraFinalColorPolicies( - std::vector& requests) const { + std::vector& plans) const { const RenderPipelineAsset* pipelineAsset = GetPipelineAsset(); const FinalColorSettings pipelineDefaults = pipelineAsset != nullptr ? pipelineAsset->GetDefaultFinalColorSettings() : FinalColorSettings(); - for (CameraRenderRequest& request : requests) { - if (request.camera == nullptr) { + for (CameraFramePlan& plan : plans) { + if (plan.request.camera == nullptr) { continue; } - request.finalColorPolicy = ResolveFinalColorPolicy( + plan.finalColorPolicy = ResolveFinalColorPolicy( pipelineDefaults, - &request.camera->GetFinalColorOverrides()); + &plan.request.camera->GetFinalColorOverrides()); } } void SceneRenderer::AttachFullscreenStageRequests( const RenderContext& context, - std::vector& requests) { - PrepareOwnedFullscreenStageState(requests.size()); + std::vector& plans) { + PrepareOwnedFullscreenStageState(plans.size()); - for (size_t index = 0; index < requests.size(); ++index) { - CameraRenderRequest& request = requests[index]; - if (request.camera == nullptr || - request.context.device == nullptr || - !HasValidColorTarget(request.surface)) { + for (size_t index = 0; index < plans.size(); ++index) { + CameraFramePlan& plan = plans[index]; + if (plan.request.camera == nullptr || + plan.request.context.device == nullptr || + !HasValidColorTarget(plan.request.surface)) { continue; } std::unique_ptr postProcessSequence = - BuildCameraPostProcessPassSequence(request.camera->GetPostProcessPasses()); + BuildCameraPostProcessPassSequence(plan.request.camera->GetPostProcessPasses()); std::unique_ptr finalOutputSequence = - BuildFinalColorPassSequence(request.finalColorPolicy); + BuildFinalColorPassSequence(plan.finalColorPolicy); const bool hasPostProcess = postProcessSequence != nullptr && postProcessSequence->GetPassCount() > 0u; @@ -208,14 +255,14 @@ void SceneRenderer::AttachFullscreenStageRequests( continue; } - if (request.surface.GetSampleCount() > 1u) { + if (plan.request.surface.GetSampleCount() > 1u) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "SceneRenderer fullscreen post-process/final-output chain currently requires a single-sample main scene surface"); continue; } - const std::vector& colorAttachments = request.surface.GetColorAttachments(); + const std::vector& colorAttachments = plan.request.surface.GetColorAttachments(); const RHI::Format colorFormat = colorAttachments[0]->GetFormat(); if (colorFormat == RHI::Format::Unknown) { continue; @@ -226,8 +273,8 @@ void SceneRenderer::AttachFullscreenStageRequests( if (surfaceCache == nullptr || !surfaceCache->EnsureSurfaces( context, - request.surface.GetWidth(), - request.surface.GetHeight(), + plan.request.surface.GetWidth(), + plan.request.surface.GetHeight(), colorFormat, fullscreenSurfaceCount)) { continue; @@ -246,30 +293,30 @@ void SceneRenderer::AttachFullscreenStageRequests( } if (hasPostProcess) { - request.postProcess.sourceSurface = - ConfigureFullscreenStageSurface(*sceneColorEntry, request.surface, true); - request.postProcess.sourceColorView = sceneColorEntry->shaderResourceView; - request.postProcess.sourceColorState = request.postProcess.sourceSurface.GetColorStateAfter(); - request.postProcess.destinationSurface = + plan.postProcess.sourceSurface = + ConfigureFullscreenStageSurface(*sceneColorEntry, plan.request.surface, true); + plan.postProcess.sourceColorView = sceneColorEntry->shaderResourceView; + plan.postProcess.sourceColorState = plan.postProcess.sourceSurface.GetColorStateAfter(); + plan.postProcess.destinationSurface = hasFinalOutput - ? ConfigureFullscreenStageSurface(*postProcessOutputEntry, request.surface, false) - : request.surface; + ? ConfigureFullscreenStageSurface(*postProcessOutputEntry, plan.request.surface, false) + : plan.request.surface; m_ownedPostProcessSequences[index] = std::move(postProcessSequence); - request.postProcess.passes = m_ownedPostProcessSequences[index].get(); + plan.postProcess.passes = m_ownedPostProcessSequences[index].get(); } if (hasFinalOutput) { const FullscreenPassSurfaceCache::SurfaceEntry* finalOutputSourceEntry = hasPostProcess ? postProcessOutputEntry : sceneColorEntry; - request.finalOutput.sourceSurface = + plan.finalOutput.sourceSurface = hasPostProcess - ? request.postProcess.destinationSurface - : ConfigureFullscreenStageSurface(*sceneColorEntry, request.surface, true); - request.finalOutput.sourceColorView = finalOutputSourceEntry->shaderResourceView; - request.finalOutput.sourceColorState = request.finalOutput.sourceSurface.GetColorStateAfter(); - request.finalOutput.destinationSurface = request.surface; + ? plan.postProcess.destinationSurface + : ConfigureFullscreenStageSurface(*sceneColorEntry, plan.request.surface, true); + plan.finalOutput.sourceColorView = finalOutputSourceEntry->shaderResourceView; + plan.finalOutput.sourceColorState = plan.finalOutput.sourceSurface.GetColorStateAfter(); + plan.finalOutput.destinationSurface = plan.request.surface; m_ownedFinalOutputSequences[index] = std::move(finalOutputSequence); - request.finalOutput.passes = m_ownedFinalOutputSequences[index].get(); + plan.finalOutput.passes = m_ownedFinalOutputSequences[index].get(); } } } diff --git a/engine/src/Rendering/Extraction/RenderSceneExtractor.cpp b/engine/src/Rendering/Extraction/RenderSceneExtractor.cpp index 04112972..2f19dd9d 100644 --- a/engine/src/Rendering/Extraction/RenderSceneExtractor.cpp +++ b/engine/src/Rendering/Extraction/RenderSceneExtractor.cpp @@ -3,6 +3,7 @@ #include "Components/CameraComponent.h" #include "Components/GameObject.h" #include "Components/LightComponent.h" +#include "Rendering/FrameData/RendererListUtils.h" #include "Rendering/Extraction/RenderSceneUtility.h" #include "Scene/Scene.h" @@ -125,6 +126,21 @@ bool CompareVisibleGaussianSplats( return CompareVisibleGaussianSplatsStable(lhs, rhs); } +void BuildRendererLists(RenderSceneData& sceneData) { + sceneData.cullingResults.Clear(); + sceneData.cullingResults.rendererLists.reserve(5u); + sceneData.cullingResults.rendererLists.push_back( + BuildRendererList(RendererListType::AllVisible, sceneData.visibleItems)); + sceneData.cullingResults.rendererLists.push_back( + BuildRendererList(RendererListType::Opaque, sceneData.visibleItems)); + sceneData.cullingResults.rendererLists.push_back( + BuildRendererList(RendererListType::Transparent, sceneData.visibleItems)); + sceneData.cullingResults.rendererLists.push_back( + BuildRendererList(RendererListType::ShadowCaster, sceneData.visibleItems)); + sceneData.cullingResults.rendererLists.push_back( + BuildRendererList(RendererListType::ObjectId, sceneData.visibleItems)); +} + } // namespace RenderSceneData RenderSceneExtractor::Extract( @@ -165,6 +181,7 @@ RenderSceneData RenderSceneExtractor::Extract( sceneData.visibleVolumes.begin(), sceneData.visibleVolumes.end(), CompareVisibleVolumes); + BuildRendererLists(sceneData); ExtractLighting(scene, cameraPosition, cullingMask, sceneData.lighting); return sceneData; @@ -208,6 +225,7 @@ RenderSceneData RenderSceneExtractor::ExtractForCamera( sceneData.visibleVolumes.begin(), sceneData.visibleVolumes.end(), CompareVisibleVolumes); + BuildRendererLists(sceneData); ExtractLighting(scene, cameraPosition, cullingMask, sceneData.lighting); return sceneData; diff --git a/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp b/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp index d137a0bd..2dd12cb9 100644 --- a/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp +++ b/engine/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp @@ -2,6 +2,7 @@ #include "RHI/RHICommandList.h" #include "Rendering/Extraction/RenderSceneExtractor.h" +#include "Rendering/FrameData/RendererListUtils.h" #include "Rendering/Internal/RenderSurfacePipelineUtils.h" #include "Rendering/RenderSurface.h" #include "Resources/Mesh/Mesh.h" @@ -139,13 +140,16 @@ bool BuiltinDepthStylePassBase::Execute(const RenderPassContext& context) { commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); - for (const VisibleRenderItem& visibleItem : context.sceneData.visibleItems) { - if (!ShouldRenderVisibleItem(visibleItem)) { - continue; - } + VisitRendererListVisibleItems( + context.sceneData, + GetRendererListType(), + [&](const VisibleRenderItem& visibleItem) { + if (!ShouldRenderVisibleItem(visibleItem)) { + return; + } - DrawVisibleItem(context.renderContext, context.surface, context.sceneData, visibleItem); - } + DrawVisibleItem(context.renderContext, context.surface, context.sceneData, visibleItem); + }); commandList->EndRenderPass(); @@ -165,6 +169,10 @@ bool BuiltinDepthStylePassBase::Execute(const RenderPassContext& context) { return true; } +RendererListType BuiltinDepthStylePassBase::GetRendererListType() const { + return RendererListType::AllVisible; +} + bool BuiltinDepthStylePassBase::ShouldRenderVisibleItem(const VisibleRenderItem& visibleItem) const { (void)visibleItem; return true; diff --git a/engine/src/Rendering/Passes/BuiltinGaussianSplatPass.cpp b/engine/src/Rendering/Passes/BuiltinGaussianSplatPass.cpp index 5eb4cdda..7715832c 100644 --- a/engine/src/Rendering/Passes/BuiltinGaussianSplatPass.cpp +++ b/engine/src/Rendering/Passes/BuiltinGaussianSplatPass.cpp @@ -222,6 +222,16 @@ bool BuiltinGaussianSplatPass::Initialize(const RenderContext& context) { return EnsureInitialized(context); } +bool BuiltinGaussianSplatPass::IsActive(const RenderSceneData& sceneData) const { + return !sceneData.visibleGaussianSplats.empty(); +} + +bool BuiltinGaussianSplatPass::Prepare( + const RenderContext& context, + const RenderSceneData& sceneData) { + return PrepareGaussianSplatResources(context, sceneData); +} + bool BuiltinGaussianSplatPass::PrepareGaussianSplatResources( const RenderContext& context, const RenderSceneData& sceneData) { diff --git a/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp b/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp index 60578704..f3bed488 100644 --- a/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp +++ b/engine/src/Rendering/Passes/BuiltinObjectIdPass.cpp @@ -2,6 +2,7 @@ #include "Core/Asset/ResourceManager.h" #include "RHI/RHICommandList.h" +#include "Rendering/FrameData/RendererListUtils.h" #include "Rendering/Internal/RenderSurfacePipelineUtils.h" #include "Rendering/Extraction/RenderSceneExtractor.h" #include "Rendering/RenderSurface.h" @@ -119,9 +120,12 @@ bool BuiltinObjectIdPass::Execute(const RenderPassContext& context) { commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); commandList->SetPipelineState(m_pipelineState); - for (const VisibleRenderItem& visibleItem : context.sceneData.visibleItems) { - DrawVisibleItem(context.renderContext, context.sceneData, visibleItem); - } + VisitRendererListVisibleItems( + context.sceneData, + RendererListType::ObjectId, + [&](const VisibleRenderItem& visibleItem) { + DrawVisibleItem(context.renderContext, context.sceneData, visibleItem); + }); commandList->EndRenderPass(); diff --git a/engine/src/Rendering/Passes/BuiltinShadowCasterPass.cpp b/engine/src/Rendering/Passes/BuiltinShadowCasterPass.cpp index a75c77aa..db58343f 100644 --- a/engine/src/Rendering/Passes/BuiltinShadowCasterPass.cpp +++ b/engine/src/Rendering/Passes/BuiltinShadowCasterPass.cpp @@ -22,6 +22,10 @@ const char* BuiltinShadowCasterPass::GetName() const { return "BuiltinShadowCasterPass"; } +RendererListType BuiltinShadowCasterPass::GetRendererListType() const { + return RendererListType::ShadowCaster; +} + bool BuiltinShadowCasterPass::ShouldRenderVisibleItem(const VisibleRenderItem& visibleItem) const { return visibleItem.meshRenderer == nullptr || visibleItem.meshRenderer->GetCastShadows(); } diff --git a/engine/src/Rendering/Passes/BuiltinVolumetricPass.cpp b/engine/src/Rendering/Passes/BuiltinVolumetricPass.cpp index 540ed2f0..61c696ff 100644 --- a/engine/src/Rendering/Passes/BuiltinVolumetricPass.cpp +++ b/engine/src/Rendering/Passes/BuiltinVolumetricPass.cpp @@ -210,6 +210,16 @@ bool BuiltinVolumetricPass::Initialize(const RenderContext& context) { return EnsureInitialized(context); } +bool BuiltinVolumetricPass::IsActive(const RenderSceneData& sceneData) const { + return !sceneData.visibleVolumes.empty(); +} + +bool BuiltinVolumetricPass::Prepare( + const RenderContext& context, + const RenderSceneData& sceneData) { + return PrepareVolumeResources(context, sceneData); +} + bool BuiltinVolumetricPass::PrepareVolumeResources( const RenderContext& context, const RenderSceneData& sceneData) { diff --git a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp index e36e4758..0a5048ac 100644 --- a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp +++ b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp @@ -143,58 +143,31 @@ RHI::InputLayoutDesc BuiltinForwardPipeline::BuildInputLayout() { bool BuiltinForwardPipeline::Initialize(const RenderContext& context) { return EnsureInitialized(context) && - m_gaussianSplatPass != nullptr && - m_gaussianSplatPass->Initialize(context) && - m_volumetricPass != nullptr && - m_volumetricPass->Initialize(context); + InitializeForwardSceneFeaturePasses(context); } void BuiltinForwardPipeline::Shutdown() { - if (m_gaussianSplatPass != nullptr) { - m_gaussianSplatPass->Shutdown(); - } - if (m_volumetricPass != nullptr) { - m_volumetricPass->Shutdown(); - } + ShutdownForwardSceneFeaturePasses(); DestroyPipelineResources(); } bool BuiltinForwardPipeline::Render( - const RenderContext& context, - const RenderSurface& surface, - const RenderSceneData& sceneData) { - if (!Initialize(context)) { + const FrameExecutionContext& executionContext) { + if (!Initialize(executionContext.renderContext)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinForwardPipeline::Render failed: Initialize returned false"); return false; } - if (m_volumetricPass != nullptr && - !sceneData.visibleVolumes.empty() && - !m_volumetricPass->PrepareVolumeResources(context, sceneData)) { + if (!PrepareForwardSceneFeaturePasses(executionContext)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, - "BuiltinForwardPipeline::Render failed: PrepareVolumeResources returned false"); - return false; - } - if (m_gaussianSplatPass != nullptr && - !sceneData.visibleGaussianSplats.empty() && - !m_gaussianSplatPass->PrepareGaussianSplatResources(context, sceneData)) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline::Render failed: PrepareGaussianSplatResources returned false"); + "BuiltinForwardPipeline::Render failed: PrepareForwardSceneFeaturePasses returned false"); return false; } - const RenderPassContext passContext = { - context, - surface, - sceneData, - nullptr, - nullptr, - RHI::ResourceStates::Common - }; + const RenderPassContext passContext = BuildRenderPassContext(executionContext); if (!BeginForwardScenePass(passContext)) { Debug::Logger::Get().Error( @@ -203,53 +176,33 @@ bool BuiltinForwardPipeline::Render( return false; } - const bool sampledDirectionalShadow = ShouldSampleMainDirectionalShadowMap(sceneData); + const bool sampledDirectionalShadow = + ShouldSampleMainDirectionalShadowMap(executionContext.sceneData); if (sampledDirectionalShadow) { - TransitionMainDirectionalShadowForSampling(context, sceneData); + TransitionMainDirectionalShadowForSampling( + executionContext.renderContext, + executionContext.sceneData); } - bool renderResult = ExecuteForwardOpaquePass(passContext); - if (renderResult) { - renderResult = ExecuteForwardSkyboxPass(passContext); - if (!renderResult) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline::Render failed: ExecuteForwardSkyboxPass returned false"); - } - } - if (renderResult && m_gaussianSplatPass != nullptr) { - renderResult = m_gaussianSplatPass->Execute(passContext); - if (!renderResult) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline::Render failed: BuiltinGaussianSplatPass::Execute returned false"); - } - } - if (renderResult && m_volumetricPass != nullptr) { - renderResult = m_volumetricPass->Execute(passContext); - if (!renderResult) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline::Render failed: BuiltinVolumetricPass::Execute returned false"); - } - } - if (renderResult) { - renderResult = ExecuteForwardTransparentPass(passContext); - if (!renderResult) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinForwardPipeline::Render failed: ExecuteForwardTransparentPass returned false"); - } - } + const bool renderResult = ExecuteForwardScene(executionContext); if (sampledDirectionalShadow) { - RestoreMainDirectionalShadowAfterSampling(context, sceneData); + RestoreMainDirectionalShadowAfterSampling( + executionContext.renderContext, + executionContext.sceneData); } EndForwardScenePass(passContext); return renderResult; } +bool BuiltinForwardPipeline::Render( + const RenderContext& context, + const RenderSurface& surface, + const RenderSceneData& sceneData) { + return Render(FrameExecutionContext(context, surface, sceneData)); +} + bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& passContext) { const RenderContext& context = passContext.renderContext; const RenderSurface& surface = passContext.surface; @@ -358,20 +311,18 @@ void BuiltinForwardPipeline::EndForwardScenePass(const RenderPassContext& passCo } } -bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& passContext) { - const RenderContext& context = passContext.renderContext; - const RenderSurface& surface = passContext.surface; - const RenderSceneData& sceneData = passContext.sceneData; - - return DrawVisibleItems(context, surface, sceneData, false); +bool BuiltinForwardPipeline::ExecuteForwardOpaquePass( + const ScenePhaseExecutionContext& executionContext) { + return DrawVisibleItems( + executionContext.frameContext, + BuildDrawSettings(executionContext.scenePhase)); } -bool BuiltinForwardPipeline::ExecuteForwardTransparentPass(const RenderPassContext& passContext) { - const RenderContext& context = passContext.renderContext; - const RenderSurface& surface = passContext.surface; - const RenderSceneData& sceneData = passContext.sceneData; - - return DrawVisibleItems(context, surface, sceneData, true); +bool BuiltinForwardPipeline::ExecuteForwardTransparentPass( + const ScenePhaseExecutionContext& executionContext) { + return DrawVisibleItems( + executionContext.frameContext, + BuildDrawSettings(executionContext.scenePhase)); } bool BuiltinForwardPipeline::EnsureInitialized(const RenderContext& context) { @@ -392,6 +343,146 @@ bool BuiltinForwardPipeline::EnsureInitialized(const RenderContext& context) { return m_initialized; } +BuiltinForwardPipeline::ForwardSceneFeaturePassArray +BuiltinForwardPipeline::CollectForwardSceneFeaturePasses() const { + return { + m_gaussianSplatPass.get(), + m_volumetricPass.get() + }; +} + +bool BuiltinForwardPipeline::InitializeForwardSceneFeaturePasses(const RenderContext& context) { + for (SceneRenderFeaturePass* featurePass : CollectForwardSceneFeaturePasses()) { + if (featurePass == nullptr || !featurePass->Initialize(context)) { + return false; + } + } + + return true; +} + +void BuiltinForwardPipeline::ShutdownForwardSceneFeaturePasses() { + for (SceneRenderFeaturePass* featurePass : CollectForwardSceneFeaturePasses()) { + if (featurePass != nullptr) { + featurePass->Shutdown(); + } + } +} + +bool BuiltinForwardPipeline::PrepareForwardSceneFeaturePasses( + const FrameExecutionContext& executionContext) const { + for (SceneRenderFeaturePass* featurePass : CollectForwardSceneFeaturePasses()) { + if (featurePass == nullptr || !featurePass->IsActive(executionContext.sceneData)) { + continue; + } + + if (!featurePass->Prepare( + executionContext.renderContext, + executionContext.sceneData)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinForwardPipeline feature prepare failed: ") + + featurePass->GetName()).CStr()); + return false; + } + } + + return true; +} + +bool BuiltinForwardPipeline::ExecuteForwardSceneFeaturePasses( + const ScenePhaseExecutionContext& executionContext) const { + const RenderPassContext passContext = BuildRenderPassContext(executionContext); + + for (SceneRenderFeaturePass* featurePass : CollectForwardSceneFeaturePasses()) { + if (featurePass == nullptr || + !featurePass->IsActive(executionContext.frameContext.sceneData)) { + continue; + } + + if (!featurePass->Execute(passContext)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinForwardPipeline feature execute failed: ") + + featurePass->GetName()).CStr()); + return false; + } + } + + return true; +} + +ScenePhaseExecutionContext BuiltinForwardPipeline::BuildScenePhaseExecutionContext( + const FrameExecutionContext& executionContext, + ScenePhase scenePhase) const { + return ScenePhaseExecutionContext( + executionContext, + scenePhase, + ShouldSampleMainDirectionalShadowMap(executionContext.sceneData)); +} + +DrawSettings BuiltinForwardPipeline::BuildDrawSettings(ScenePhase scenePhase) const { + DrawSettings drawSettings = {}; + drawSettings.scenePhase = scenePhase; + switch (scenePhase) { + case ScenePhase::Opaque: + drawSettings.rendererListType = RendererListType::Opaque; + break; + case ScenePhase::Transparent: + drawSettings.rendererListType = RendererListType::Transparent; + break; + default: + drawSettings.rendererListType = RendererListType::AllVisible; + break; + } + + return drawSettings; +} + +bool BuiltinForwardPipeline::ExecuteScenePhase( + const ScenePhaseExecutionContext& executionContext) { + switch (executionContext.scenePhase) { + case ScenePhase::Opaque: + return ExecuteForwardOpaquePass(executionContext); + case ScenePhase::Skybox: + return ExecuteForwardSkyboxPass(BuildRenderPassContext(executionContext)); + case ScenePhase::Feature: + return ExecuteForwardSceneFeaturePasses(executionContext); + case ScenePhase::Transparent: + return ExecuteForwardTransparentPass(executionContext); + default: + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinForwardPipeline::ExecuteScenePhase does not support scene phase: ") + + ToString(executionContext.scenePhase)).CStr()); + return false; + } +} + +bool BuiltinForwardPipeline::ExecuteForwardScene( + const FrameExecutionContext& executionContext) { + static constexpr ScenePhase kForwardScenePhases[] = { + ScenePhase::Opaque, + ScenePhase::Skybox, + ScenePhase::Feature, + ScenePhase::Transparent + }; + + for (ScenePhase scenePhase : kForwardScenePhases) { + const ScenePhaseExecutionContext scenePhaseExecutionContext = + BuildScenePhaseExecutionContext(executionContext, scenePhase); + if (!ExecuteScenePhase(scenePhaseExecutionContext)) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + (Containers::String("BuiltinForwardPipeline::ExecuteForwardScene failed during phase: ") + + ToString(scenePhase)).CStr()); + return false; + } + } + + return true; +} + bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& context) { m_builtinForwardShader = Resources::ResourceManager::Get().Load( Resources::GetBuiltinForwardLitShaderPath()); diff --git a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineResources.cpp b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineResources.cpp index b00548cf..6c03ea39 100644 --- a/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineResources.cpp +++ b/engine/src/Rendering/Pipelines/Internal/BuiltinForwardPipelineResources.cpp @@ -5,6 +5,7 @@ #include "RHI/RHICommandList.h" #include "RHI/RHIDevice.h" #include "Rendering/Builtin/BuiltinPassLayoutUtils.h" +#include "Rendering/FrameData/RendererListUtils.h" #include "Rendering/Internal/RenderSurfacePipelineUtils.h" #include "Rendering/Internal/ShaderVariantUtils.h" #include "Rendering/Materials/RenderMaterialResolve.h" @@ -687,10 +688,12 @@ BuiltinForwardPipeline::AdditionalLightConstants BuiltinForwardPipeline::BuildAd } bool BuiltinForwardPipeline::DrawVisibleItem( - const RenderContext& context, - const RenderSurface& surface, - const RenderSceneData& sceneData, + const FrameExecutionContext& executionContext, const VisibleRenderItem& visibleItem) { + const RenderContext& context = executionContext.renderContext; + const RenderSurface& surface = executionContext.surface; + const RenderSceneData& sceneData = executionContext.sceneData; + (void)surface; const RenderResourceCache::CachedMesh* cachedMesh = m_resourceCache.GetOrCreateMesh(m_device, visibleItem.mesh); if (cachedMesh == nullptr || cachedMesh->vertexBufferView == nullptr) { @@ -861,36 +864,35 @@ bool BuiltinForwardPipeline::DrawVisibleItem( } bool BuiltinForwardPipeline::DrawVisibleItems( - const RenderContext& context, - const RenderSurface& surface, - const RenderSceneData& sceneData, - bool drawTransparentItems) { + const FrameExecutionContext& executionContext, + const DrawSettings& drawSettings) { + const RenderContext& context = executionContext.renderContext; + const RenderSurface& surface = executionContext.surface; + const RenderSceneData& sceneData = executionContext.sceneData; + RHI::RHICommandList* commandList = context.commandList; RHI::RHIPipelineState* currentPipelineState = nullptr; - for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) { - const bool isTransparentItem = IsTransparentRenderQueue(visibleItem.renderQueue); - if (isTransparentItem != drawTransparentItems) { - continue; - } + auto drawVisibleItem = [&](const VisibleRenderItem& visibleItem) { const Resources::Material* material = ResolveMaterial(visibleItem); BuiltinMaterialPass pass = BuiltinMaterialPass::ForwardLit; if (!TryResolveSurfacePassType(material, pass)) { - continue; + return; } RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material); if (pipelineState == nullptr) { - continue; + return; } if (pipelineState != currentPipelineState) { commandList->SetPipelineState(pipelineState); currentPipelineState = pipelineState; } - DrawVisibleItem(context, surface, sceneData, visibleItem); - } + DrawVisibleItem(executionContext, visibleItem); + }; + VisitRendererListVisibleItems(sceneData, drawSettings.rendererListType, drawVisibleItem); return true; } diff --git a/engine/src/Rendering/Planning/SceneRenderRequestPlanner.cpp b/engine/src/Rendering/Planning/SceneRenderRequestPlanner.cpp index 6f47c63a..e96cec13 100644 --- a/engine/src/Rendering/Planning/SceneRenderRequestPlanner.cpp +++ b/engine/src/Rendering/Planning/SceneRenderRequestPlanner.cpp @@ -81,11 +81,19 @@ std::vector SceneRenderRequestPlanner::BuildRequests( ? static_cast(surface.GetRenderAreaWidth()) / static_cast(surface.GetRenderAreaHeight()) : 1.0f; + DirectionalShadowPlanningSettings effectiveShadowSettings = + m_directionalShadowPlanningSettings; + if (mainDirectionalLight->GetOverridesDirectionalShadowSettings()) { + effectiveShadowSettings.sampling = + mainDirectionalLight->GetDirectionalShadowSamplingSettings(); + effectiveShadowSettings.casterBias = + mainDirectionalLight->GetDirectionalShadowCasterBiasSettings(); + } request.directionalShadow = Internal::BuildDirectionalShadowRenderPlan( scene, *camera, *mainDirectionalLight, - m_directionalShadowPlanningSettings, + effectiveShadowSettings, viewportAspect); if (request.directionalShadow.IsValid()) { request.shadowCaster.clearFlags = RenderClearFlags::Depth;