6.4 KiB
Scene Extraction And Builtin Forward Pipeline
这条链路现在怎么工作
当前 XCEngine 的主场景渲染链路可以按下面的顺序理解:
SceneRenderer负责组织相机请求和执行顺序。CameraRenderer选择主渲染管线;默认情况下,它通过BuiltinForwardPipelineAsset创建BuiltinForwardPipeline。RenderSceneExtractor把 scene/game-object/component 结构压平成RenderSceneData。BuiltinForwardPipeline把RenderSceneData绘制到RenderSurface。
这里的关键点不是“有没有前向渲染”,而是场景数据和绘制数据已经被拆成两层:
- 提取阶段负责从场景组织里收集相机、光照和可绘制项。
- pipeline 阶段只消费已经整理好的
RenderSceneData。
不过这里讲的只是主场景绘制槽位,不是整次相机提交的完整尾段。按 engine/src/Rendering/CameraRenderer.cpp 的当前实现,BuiltinForwardPipeline 前后还可能包着:
preScenePasses- object-id pass
postScenePassesoverlayPasses
Scene View 的网格、选中描边和 editor overlay,当前就是先在 Editor 层组装成这些 request 级序列,再交给 CameraRenderer 执行。
RenderSceneData 现在包含什么
RenderSceneExtractor 当前产出的核心数据有三块:
camera与cameraDatalightingvisibleItems
其中 visibleItems 的元素类型是 VisibleRenderItem。头文件里仍保留 VisibleRenderObject = VisibleRenderItem 的兼容别名,但当前实现和新文档都应以 visibleItems 为准,而不是旧说法 visibleObjects。
每个 VisibleRenderItem 除了 mesh 和变换,还已经携带:
materialmaterialIndexsectionIndexhasSectionrenderQueuecameraDistanceSq
这让后续 pipeline 不需要回到场景层重新做组件查询。
为什么还要保留 scene extraction
游戏对象系统天然偏向层级、组件和编辑器语义;渲染系统天然偏向“当前相机要画哪些项”。RenderSceneExtractor 的价值就是把这两种数据形状解耦。
当前 extractor 已经做的事情包括:
- 选择相机并生成
RenderCameraData - 提取主方向光数据
- 递归收集满足基础绘制条件的可见项
- 产出
visibleItems并带上 material / section / distance 等渲染侧字段
它还没有做完整的商业级可见性系统,例如:
- 视锥裁剪
- 遮挡裁剪
- instancing / batching
- 透明排序主路径
BuiltinForwardPipeline 的当前定位
BuiltinForwardPipeline 现在是默认主渲染路径,但它内部已经不是旧的固定绑定模型,而是三层缓存:
- 按
(shader*, passName)缓存PassResourceLayout - 按
(shader*, passName, render state)缓存RHIPipelineState - 按
(passLayout, setIndex, objectId, material)缓存动态 descriptor set
此外它已经接入 RenderPassSequence:
- 构造时注册
BuiltinForwardOpaquePass Initialize()走 sequence 生命周期Render()先构造RenderPassContext,再执行 sequenceShutdown()也走 sequence,再由 opaque pass 回调资源清理
所以当前 builtin forward 更像“一条默认可运行的 pass-sequence 管线”,而不是过去那种把所有逻辑揉在一个固定 layout 里的实现。
Shader pass 资源契约
当前 builtin forward 对 shader pass 的资源声明有明确白名单。可识别语义只有:
PerObject:必需,单个 CBVMaterial:可选,单个 CBVBaseColorTexture:可选,单个Texture2D或TextureCubeLinearClampSampler:可选,单个 sampler
如果 shader pass 没有声明 resources,则会回退到 legacy builtin forward 绑定:
set 1 binding 0->PerObjectset 2 binding 0->Materialset 3 binding 0->BaseColorTextureset 4 binding 0->LinearClampSampler
如果声明不合法,例如未知语义、同 set 混用 sampler 和非 sampler、重复 binding 或缺少 PerObject,pipeline 会拒绝为该 pass 创建布局。
测试 tests/Rendering/unit/test_builtin_forward_pipeline.cpp 还明确校验了 builtin shader ForwardLit pass 的这组资源契约,以及 BuildInputLayout() 现在使用 POSITION=float3。
单个可见项如何被画出来
当 BuiltinForwardOpaquePass 遍历 RenderSceneData::visibleItems 时,每个物体大致会经历以下步骤:
- 根据材质解析真正要用的 shader/pass;没有合适 pass 时回退到 builtin forward shader。
- 取得 mesh 的 GPU 资源缓存。
- 生成
PerObjectConstants和PerMaterialConstants。 - 获取或创建当前 shader pass 的
PassResourceLayout。 - 逐 set 绑定 descriptor:
- 含
PerObject / Material / Texture的 set 走动态缓存。 - 只含 sampler 的 set 走静态缓存。
- 含
- 按 section 或整 mesh 发出 draw call。
BaseColorTexture 如果解析不到,会回退到初始化阶段创建的 1x1 白色纹理 SRV。
RenderSurface 在这里的作用
RenderSurface 不是单纯的附件容器,而是当前这一帧输出目标的完整描述。builtin forward 会消费它的:
- 颜色附件
- 深度附件
- render area
- clear color override
- 自动状态切换开关
colorStateBefore/colorStateAfter
因此这条主渲染路径既能服务于窗口 back buffer,也能服务于 editor viewport、离屏 RT 或其它自定义输出目标。
当前限制
- 主场景默认只有 builtin forward opaque pass。
- 透明、阴影、延迟渲染和更复杂的资源依赖管理还不在这条链路里。
RenderPassSequence目前只提供顺序执行,不是 render graph。